Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d48fbb6
Merge pull request #4 from chennyjen/longhold
chennyjen Nov 21, 2016
f68ccf5
two finger swipe right basic impl
mvlugt Nov 21, 2016
d44e7be
Merge branch 'two_finger_swipe'
mvlugt Nov 21, 2016
668ba4d
slowed down tutorial and training speech rates and fixed hold issues …
Nov 21, 2016
51f9c0c
swap values bug fix
mvlugt Nov 21, 2016
7bdb794
Merge pull request #6 from chennyjen/tutorial-fixes
chennyjen Nov 21, 2016
54a3c7a
Made longHold fire after 10 seconds instead of 4
chennyjen Nov 21, 2016
8417945
add the testing document
ryancesiel Nov 21, 2016
ff88ba4
Merge branch 'master' of https://github.com/chennyjen/VInput
ryancesiel Nov 21, 2016
1a23534
Updating formatting on TESTING.md
ryancesiel Nov 21, 2016
f5e2a10
update README with beta features
ryancesiel Nov 21, 2016
d4b76a5
Merge branch 'master' of https://github.com/chennyjen/VInput
ryancesiel Nov 21, 2016
b80b43d
Updating information in README.md
ryancesiel Nov 21, 2016
955079d
Add link to Beta Release Features document
ryancesiel Nov 21, 2016
2779e3b
punctuation keyboard impl
mvlugt Dec 11, 2016
a3e2c0f
most common words keyboard alive
mvlugt Dec 11, 2016
23c65e3
Fixed pronunciation of letter a in all modes
Dec 12, 2016
78af729
Merge pull request #7 from chennyjen/jc/letter-pronunciation
chennyjen Dec 12, 2016
ed167da
increments/decrements for last word when keyboard pinch closes/re-opens
mvlugt Dec 12, 2016
2edb9ce
announce closing keyboard
mvlugt Dec 12, 2016
9f74255
Merge branch 'punctuation'
mvlugt Dec 12, 2016
94b5e9b
Merge branch 'common'
mvlugt Dec 12, 2016
a253cdc
emoji bug fix
mvlugt Dec 12, 2016
06c88ab
fix speech from breaking; fix speech at end of swiping
ryancesiel Dec 12, 2016
2ea4d91
punctuation speech fix
mvlugt Dec 12, 2016
c2e4297
readme
mvlugt Dec 12, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,32 @@ As defined in our Alpha release project plan, below are the deliverables we have
* Usability Testing Procedure and Results ([Google Drive](https://drive.google.com/drive/u/1/folders/0Bx_oMYCmW6jGZlgxR2RQTWlnYTg))
* Feature Discovery ([Google Drive](https://drive.google.com/drive/u/1/folders/0Bx_oMYCmW6jGZlgxR2RQTWlnYTg))
* "Hello World" full screen keyboard (GitHub)

## Beta Release
As defined in our Beta release project plan, below are the deliverables we have completed. [Additional and more detailed information can be found here](https://docs.google.com/document/d/1ckNWmpqcP-tZ3jRCnp_ZpskCHK__58Kwda_WcLUEZXA/edit?usp=sharing).
* Algorithm logic
* The `Values` interface and subclasses primarily handle this logic
* Swiping/gesture setup
* Gestures registered in `KeyboardViewController.swift` include: swipe left, swipe right, swipe up, swipe down, hold, two finger swipe right, three finger swipe right, double tap, two finger hold, two finger tap, and pinch
* Gesture processing in `KeyboardViewController.swift` and primarily different mode files (i.e. `InputMode.swift`)
* Layout of keyboard
* Different UIView elements make up the visual interface within `KeyboardViewController.swift`
* Controller development and input processing
* `KeyboardViewController.swift` handles this in conjunction with different mode files (i.e. `InputMode.swift`)
* Voice prompting
* Pervasive use throughout the project of AVFoundation's `AVSpeechSynthesizer`
* Keyboard activation
* Initialization and setup upon triggered activation is found within `viewDidLoad()` of `KeyboardViewController.swift`
* "Happy path" testing: see TESTING.md

We also went beyond these specified items in our project plan in completing the following:
* **VoiceOver Integration:** UI setup to switch between VoiceOver and VInput gestures and speech announcements
* **Tutorial and Training Modes:** informative and interactive guide and steps to using VInput
* **Multiple Alphabets:** users can two finger swipe between lowercase, uppercase, basic emoji and numeric alphabets
* **Fault Tolerance:** resiliency and correction against crashes, errors, faults by reloading in memory the last word where the user left off (allowing the user to continue where they left off)
* **Ensuring Correct Input:** correctly places the input cursor at the rightmost location in the text field to prevent accidental deletion and correct appending of additional characters
* **CoreData Implementation:** as a user types right now, the words they type are stored on-device for later use in developing prediction features
* **Code Quality:** we spent significant time in the design of our code such that it is readable, maintainable, and extendable (i.e. inheritance in different input modes and alphabet values)

## Gama Release
https://docs.google.com/a/umich.edu/document/d/1XTVoPCVIVRVMSoKFGWaqs9isXvAEo0pUQTC9SAfd9gM/edit?usp=sharing
67 changes: 62 additions & 5 deletions Swift/VInput Keyboard/EmojiValues.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,70 @@

import Foundation

class EmojiValues : InsertableValues {
class EmojiValues : Values {

let emojiValues : [String] = ["😡","☹","😐","😬","😃"]
let emojiValueNames: [String] = ["Angry", "Sad", "Neutral", "Grinning", "Very Happy"]
let emojiValues : [String] = ["😃","😊","😬","😐","☹","😭","😡"]
var index: Int
var valueType: ValueUtil.VALUE_TYPE

override init(values: [String] = [], valueType: ValueUtil.VALUE_TYPE = .emoji) {
super.init(values: emojiValues, valueType: valueType)
init(values: [String] = [], valueType: ValueUtil.VALUE_TYPE = .emoji) {
self.index = 0
self.valueType = .emoji
}

func shiftLeft()
{
if index > 0
{
index -= 1
}
}

func shiftRight()
{
if index < emojiValues.count - 1
{
index += 1
}
}

func getCurrentValue() -> String
{
return emojiValues[index]
}

func resetIndexes()
{
index = 0
}

func getLeftIndex() -> Int
{
return index
}

func getRightIndex() -> Int
{
return index
}

func isSearchingResetAndAnounce() -> Bool
{
if index != 0 {
index = 0
return true
}
return false
}

func getValueType() -> ValueUtil.VALUE_TYPE
{
return valueType
}

func isDone() -> Bool
{
return index == (emojiValues.count - 1)
}

}
118 changes: 108 additions & 10 deletions Swift/VInput Keyboard/InputMode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ class InputMode : Mode {
var keyboardController: KeyboardViewController!
let MODE_NAME = "InputMode"
var currentWord: String = ""
var swapBack: Bool = false

init(keyboardController: KeyboardViewController) {
// self.values = values
self.keyboardController = keyboardController
self.swapBack = false
}

func initialize() {
Expand All @@ -34,6 +35,25 @@ class InputMode : Mode {
let textBeforeMarker: String? = keyboardController.textDocumentProxy.documentContextBeforeInput
if textBeforeMarker != nil && textBeforeMarker!.characters.last != " " {
currentWord = loadFromProxy()

//Need to decrement here - This is repeat code for now - same as swipe down
let context = self.keyboardController.persistentContainer!.viewContext
let request = NSFetchRequest<NSFetchRequestResult>()
request.predicate = NSPredicate(format: "word = %@", currentWord)
request.entity = NSEntityDescription.entity(forEntityName: "TypedWord", in: context)

do {
let results = try context.fetch(request)
let wordToInsertOrUpdate: TypedWord?
if results.count > 0 {
wordToInsertOrUpdate = (results[0] as! TypedWord)
wordToInsertOrUpdate!.frequency -= 1
try context.save()
}
} catch {
let fetchError = error as NSError
print(fetchError)
}
}
VisualUtil.updateViewAndAnnounce(letter: keyboardController.currentValues.getCurrentValue())
}
Expand All @@ -43,19 +63,35 @@ class InputMode : Mode {
}

func onSwipeLeft() {
if keyboardController.currentValues.getValueType() == ValueUtil.VALUE_TYPE.uppercase {
if swapBack && keyboardController.currentValues.getValueType() == ValueUtil.VALUE_TYPE.uppercase {
ValueUtil.swapMode(keyboardController: keyboardController, valueType: ValueUtil.VALUE_TYPE.lowercase)
swapBack = false
}
keyboardController.currentValues.shiftLeft()
VisualUtil.updateViewAndAnnounce(letter: keyboardController.currentValues.getCurrentValue())
if keyboardController.currentValues.isDone() {
SpeechUtil.speak(textToSpeak: keyboardController.currentValues.getCurrentValue())
SpeechUtil.speak(textToSpeak: "Swipe up to insert. Swipe down to reset.")
let systemSoundID: SystemSoundID = 4095
AudioServicesPlaySystemSound (systemSoundID)
} else {
VisualUtil.updateViewAndAnnounce(letter: keyboardController.currentValues.getCurrentValue())
}
}

func onSwipeRight() {
if keyboardController.currentValues.getValueType() == ValueUtil.VALUE_TYPE.uppercase {
if swapBack && keyboardController.currentValues.getValueType() == ValueUtil.VALUE_TYPE.uppercase {
ValueUtil.swapMode(keyboardController: keyboardController, valueType: ValueUtil.VALUE_TYPE.lowercase)
swapBack = false
}
if keyboardController.currentValues.isDone() {
SpeechUtil.speak(textToSpeak: keyboardController.currentValues.getCurrentValue())
SpeechUtil.speak(textToSpeak: "Swipe up to insert. Swipe down to reset.")
let systemSoundID: SystemSoundID = 4095
AudioServicesPlaySystemSound (systemSoundID)
} else {
keyboardController.currentValues.shiftRight()
VisualUtil.updateViewAndAnnounce(letter: keyboardController.currentValues.getCurrentValue())
}
keyboardController.currentValues.shiftRight()
VisualUtil.updateViewAndAnnounce(letter: keyboardController.currentValues.getCurrentValue())
}

func onSwipeUp() {
Expand All @@ -64,11 +100,15 @@ class InputMode : Mode {
if keyboardController.currentValues.getValueType() == ValueUtil.VALUE_TYPE.uppercase{
text = "Inserting upper case " + keyboardController.currentValues.getCurrentValue()
}
if keyboardController.currentValues.getValueType() == ValueUtil.VALUE_TYPE.lowercase {
text = "Inserting " + keyboardController.currentValues.getCurrentValue().uppercased()
}
SpeechUtil.speak(textToSpeak: text)
currentWord.append(keyboardController.currentValues.getCurrentValue())
keyboardController.textDocumentProxy.insertText(keyboardController.currentValues.getCurrentValue())
if keyboardController.currentValues.getValueType() == ValueUtil.VALUE_TYPE.uppercase{
if swapBack && keyboardController.currentValues.getValueType() == ValueUtil.VALUE_TYPE.uppercase{
ValueUtil.swapMode(keyboardController: keyboardController, valueType: ValueUtil.VALUE_TYPE.lowercase)
swapBack = false
}
keyboardController.currentValues.resetIndexes()
VisualUtil.updateViewAndAnnounce(letter: keyboardController.currentValues.getCurrentValue())
Expand Down Expand Up @@ -117,23 +157,45 @@ class InputMode : Mode {
else if keyboardController.textDocumentProxy.documentContextBeforeInput?.characters.last != " " {
currentWord = loadFromProxy()

//reload word here and decrement from count
let context = self.keyboardController.persistentContainer!.viewContext
let request = NSFetchRequest<NSFetchRequestResult>()
request.predicate = NSPredicate(format: "word = %@", currentWord)
request.entity = NSEntityDescription.entity(forEntityName: "TypedWord", in: context)

do {
let results = try context.fetch(request)
let wordToInsertOrUpdate: TypedWord?
if results.count > 0 {
wordToInsertOrUpdate = (results[0] as! TypedWord)
wordToInsertOrUpdate!.frequency -= 1
try context.save()
}
} catch {
let fetchError = error as NSError
print(fetchError)
}
}
}
if keyboardController.currentValues.getValueType() == ValueUtil.VALUE_TYPE.uppercase{
if swapBack && keyboardController.currentValues.getValueType() == ValueUtil.VALUE_TYPE.uppercase {
ValueUtil.swapMode(keyboardController: keyboardController, valueType: ValueUtil.VALUE_TYPE.lowercase)
swapBack = false
}
VisualUtil.updateViewAndAnnounce(letter: keyboardController.currentValues.getCurrentValue())
}

func onHold() {
//TODO: This needs to be refactored throughout
if keyboardController.currentValues.getValueType() == .lowercase {
ValueUtil.swapMode(keyboardController: keyboardController, valueType: ValueUtil.VALUE_TYPE.uppercase)
VisualUtil.updateView(letter: keyboardController.currentValues.getCurrentValue())
swapBack = true
SpeechUtil.speak(textToSpeak: "Current letter upper cased" )
}
else if keyboardController.currentValues.getValueType() == .uppercase {
ValueUtil.swapMode(keyboardController: keyboardController, valueType: ValueUtil.VALUE_TYPE.lowercase)
VisualUtil.updateView(letter: keyboardController.currentValues.getCurrentValue())
swapBack = false
SpeechUtil.speak(textToSpeak: "Current letter lower cased")
}
}
Expand Down Expand Up @@ -198,10 +260,15 @@ class InputMode : Mode {


func onTwoTouchTap() {
SpeechUtil.speak(textToSpeak: "Left or right of " + keyboardController.currentValues.getCurrentValue())
if keyboardController.currentValues.getValueType() == ValueUtil.VALUE_TYPE.lowercase {
SpeechUtil.speak(textToSpeak: "Left or right of " + keyboardController.currentValues.getCurrentValue().uppercased())
}
else {
SpeechUtil.speak(textToSpeak: "Left or right of " + keyboardController.currentValues.getCurrentValue())
}
}

func onTwoTouchHold(){
func onTwoTouchHold() {
var text = ""
if (currentWord != ""){
text = "Current word: " + currentWord
Expand All @@ -213,6 +280,37 @@ class InputMode : Mode {
}


func onTwoFingerSwipeRight() {

let currentValueType: ValueUtil.VALUE_TYPE = keyboardController.currentValues.getValueType()
let numValueTypes: Int = ValueUtil.VALUE_TYPE.numValueTypes(currentValueType)() + 1
ValueUtil.swapMode(keyboardController: keyboardController, valueType: ValueUtil.VALUE_TYPE(rawValue: ((currentValueType.rawValue + 1) % numValueTypes))!)

//TODO: Clean and refactor this
let valHolder: Int = keyboardController.currentValues.getValueType().rawValue
var text: String = "Switching to "
switch valHolder {
case 0:
text += "lower case alphabet"
case 1:
text += "upper case alphabet"
case 2:
text += "numbers 0 through 9"
case 3:
text += "emoticons"
case 4:
text += "punctuation"
case 5:
text += "your most common words"
default:
break
}
SpeechUtil.speak(textToSpeak: text)

keyboardController.currentValues.resetIndexes()
VisualUtil.updateViewAndAnnounce(letter: keyboardController.currentValues.getCurrentValue())

}

private func loadFromProxy() -> String {
let textInDocumentProxy : [String] = keyboardController.textDocumentProxy.documentContextBeforeInput!.components(separatedBy: " ").filter{$0.isEmpty == false}
Expand Down
Loading