Skip to content

Commit

Permalink
Merge pull request #20 from jhrcook/haptics
Browse files Browse the repository at this point in the history
Haptics
  • Loading branch information
jhrcook authored Feb 1, 2021
2 parents 943b262 + 2605b4a commit 6219d21
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 21 deletions.
66 changes: 66 additions & 0 deletions Workout Spinner WatchKit Extension/Models/HapticsSettings.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// HapticsSettings.swift
// Workout Spinner WatchKit Extension
//
// Created by Joshua on 1/29/21.
// Copyright © 2021 Joshua Cook. All rights reserved.
//

import SwiftUI
import WatchKit

struct HapticsSettings {
var successfulWheelSpin: Bool = setting(for: .successfulWheelSpin)
var startExercise: Bool = setting(for: .startExercise)
var endExercise: Bool = setting(for: .endExercise)

init() {
checkInitialRegistration()
}

enum HapticSetting: String, CaseIterable {
case successfulWheelSpin
case startExercise
case endExercise
}

public func saveAll() {
save(.successfulWheelSpin, as: successfulWheelSpin)
save(.startExercise, as: startExercise)
save(.endExercise, as: endExercise)
}

public func save(_ setting: HapticSetting, as value: Bool) {
UserDefaults.standard.setValue(value, forKey: setting.rawValue)
}

internal static func setting(for setting: HapticSetting) -> Bool {
UserDefaults.standard.bool(forKey: setting.rawValue)
}

private func checkInitialRegistration() {
let initialCheckKey = "HapticSettingsInitialCheck"
if UserDefaults.standard.bool(forKey: initialCheckKey) { return }
for key in HapticSetting.allCases {
UserDefaults.standard.setValue(true, forKey: key.rawValue)
}
UserDefaults.standard.setValue(true, forKey: initialCheckKey)
}

public func play(soundFor action: HapticSetting) {
switch action {
case .successfulWheelSpin:
if successfulWheelSpin {
WKInterfaceDevice.current().play(.success)
}
case .startExercise:
if startExercise {
WKInterfaceDevice.current().play(.start)
}
case .endExercise:
if endExercise {
WKInterfaceDevice.current().play(.stop)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,16 @@
//

import Foundation
import WatchKit

class WheelVelocityTracker: ObservableObject {
let velocityThreshold: Double
let memory: Int
private var history = [Double]()
var velocityThreshold: Double = 50.0
private(set) var didPassThreshold: Bool = false
var memory: Int = 10
private(set) var currentVelocity = 0.0

init() {}

init(memory: Int) {
self.memory = memory
}

init(velocityThreshold: Double) {
self.velocityThreshold = velocityThreshold
}

init(velocityThreshold: Double, memory: Int) {
init(velocityThreshold: Double = 5, memory: Int = 3) {
self.velocityThreshold = velocityThreshold
self.memory = memory
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ extension ExercisePicker {

func rotationEffectDidFinish() {
if velocityTracker.didPassThreshold {
haptics.play(soundFor: .successfulWheelSpin)
exerciseSelected = true
velocityTracker.resetThreshold()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ struct ExercisePicker: View {
@ObservedObject var workoutManager: WorkoutManager
@ObservedObject var exerciseOptions: ExerciseOptions

let haptics = HapticsSettings()

var numExercises: Int {
exerciseOptions.exercisesBlacklistFiltered.count
}
Expand All @@ -29,7 +31,7 @@ struct ExercisePicker: View {

// Spinning wheel constants.
var spinDirection: Double {
return WKInterfaceDevice().crownOrientation == .left ? 1.0 : -1.0
return WKInterfaceDevice.current().crownOrientation == .left ? 1.0 : -1.0
}

internal var crownVelocityMultiplier = UserDefaults.readCrownVelocityMultiplier()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

import SwiftUI
import WatchKit

struct ExerciseStartView: View {
@ObservedObject var workoutManager: WorkoutManager
Expand All @@ -32,6 +33,8 @@ struct ExerciseStartView: View {
return intensity.rawValue
}

let haptics = HapticsSettings()

@Environment(\.presentationMode) var presentationMode

init(workoutManager: WorkoutManager, exerciseCanceled: Binding<Bool>) {
Expand Down Expand Up @@ -75,6 +78,7 @@ struct ExerciseStartView: View {
if self.timeRemaining > 0 {
self.timeRemaining -= 1
} else if self.timeRemaining <= 0 {
haptics.play(soundFor: .startExercise) // haptic feedback
exerciseCanceled = false
self.presentationMode.wrappedValue.dismiss()
}
Expand Down
14 changes: 11 additions & 3 deletions Workout Spinner WatchKit Extension/Views/ExerciseView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
//

import SwiftUI
import WatchKit

struct ExerciseView: View {
@ObservedObject var workoutManager: WorkoutManager
@ObservedObject var workoutTracker: WorkoutTracker
@Binding private var exerciseComplete: Bool
var workoutInfo: ExerciseInfo?

let haptics = HapticsSettings()
let intensity: ExerciseIntensity = ExerciseStartView.loadExerciseIntensity()

init(workoutManager: WorkoutManager, workoutTracker: WorkoutTracker, exerciseComplete: Binding<Bool>) {
Expand Down Expand Up @@ -101,8 +102,13 @@ struct ExerciseView: View {
extension ExerciseView {
/// Called when the exercise is complete and the 'Done" button is tapped.
func finishExercise() {
haptics.play(soundFor: .endExercise) // haptic feedback

// Add data from exercise to the workout tracker and clear the info from the workout manager.
workoutTracker.addData(info: workoutManager.exerciseInfo!, duration: Double(workoutManager.elapsedSeconds), activeCalories: workoutManager.activeCalories, heartRate: workoutManager.allHeartRateReadings)
workoutTracker.addData(info: workoutManager.exerciseInfo!,
duration: Double(workoutManager.elapsedSeconds),
activeCalories: workoutManager.activeCalories,
heartRate: workoutManager.allHeartRateReadings)
workoutManager.resetTrackedInformation()

exerciseComplete = true
Expand All @@ -122,7 +128,9 @@ extension ExerciseView {
static var previews: some View {
Group {
ForEach(workoutOptions.exercises) { info in
ExerciseView(workoutManager: WorkoutManager(exerciseInfo: info), workoutTracker: WorkoutTracker(), exerciseComplete: .constant(false))
ExerciseView(workoutManager: WorkoutManager(exerciseInfo: info),
workoutTracker: WorkoutTracker(),
exerciseComplete: .constant(false))
.previewDisplayName(info.displayName)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ struct Settings: View {
self.exerciseOptions = exerciseOptions
}

@State private var haptics = HapticsSettings()

var body: some View {
Form {
Section(header: SectionHeader(imageName: "figure.wave", text: "Preferences")) {
Expand Down Expand Up @@ -72,6 +74,18 @@ struct Settings: View {
PlusMinusStepper(value: $crownVelocityMultiplier, step: 1, min: 1, max: 10, label: Text("\(Int(crownVelocityMultiplier))"))
}

Section(header: SectionHeader(imageName: "applewatch.radiowaves.left.and.right", text: "Haptics")) {
Toggle(isOn: $haptics.successfulWheelSpin) {
Text("Wheel spin")
}
Toggle(isOn: $haptics.startExercise) {
Text("Begin exercise")
}
Toggle(isOn: $haptics.endExercise) {
Text("End exercise")
}
}

Section(header: SectionHeader(imageName: "info.circle", text: "About")) {
HStack {
Text("Dev")
Expand Down Expand Up @@ -120,6 +134,8 @@ extension Settings {

UserDefaults.standard.set(crownVelocityMultiplier,
forKey: UserDefaultsKeys.crownVelocityMultiplier.rawValue)

haptics.saveAll()
}

static func getSavedExerciseIntensity() -> Int {
Expand Down
12 changes: 8 additions & 4 deletions Workout Spinner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
objects = {

/* Begin PBXBuildFile section */
8404613625C4310900BFDB80 /* HapticsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8404613525C4310900BFDB80 /* HapticsSettings.swift */; };
840D3F65254C1D6200C9E2EF /* WorkoutFinishView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840D3F64254C1D6200C9E2EF /* WorkoutFinishView.swift */; };
841D04D1250648AB00786074 /* ExerciseStartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841D04D0250648AB00786074 /* ExerciseStartView.swift */; };
841D04D325064A8800786074 /* WorkoutPickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841D04D225064A8800786074 /* WorkoutPickerViewModel.swift */; };
841D04D325064A8800786074 /* ExercisePickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841D04D225064A8800786074 /* ExercisePickerViewModel.swift */; };
841D04D525064DE600786074 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841D04D425064DE600786074 /* Settings.swift */; };
841D04D925066CF400786074 /* UserDefaultsKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841D04D825066CF400786074 /* UserDefaultsKeys.swift */; };
841D04DB250673DB00786074 /* BodyPartSelectionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841D04DA250673DB00786074 /* BodyPartSelectionListView.swift */; };
Expand Down Expand Up @@ -110,9 +111,10 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
8404613525C4310900BFDB80 /* HapticsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticsSettings.swift; sourceTree = "<group>"; };
840D3F64254C1D6200C9E2EF /* WorkoutFinishView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutFinishView.swift; sourceTree = "<group>"; };
841D04D0250648AB00786074 /* ExerciseStartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExerciseStartView.swift; sourceTree = "<group>"; };
841D04D225064A8800786074 /* WorkoutPickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutPickerViewModel.swift; sourceTree = "<group>"; };
841D04D225064A8800786074 /* ExercisePickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExercisePickerViewModel.swift; sourceTree = "<group>"; };
841D04D425064DE600786074 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
841D04D825066CF400786074 /* UserDefaultsKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsKeys.swift; sourceTree = "<group>"; };
841D04DA250673DB00786074 /* BodyPartSelectionListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BodyPartSelectionListView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -348,14 +350,15 @@
8483502825403C010081D374 /* WorkoutManager.swift */,
84C1FA7B25497CD20008CE09 /* WorkoutTracker.swift */,
84E9C1002554234D002D461B /* HeartRateGraphData.swift */,
8404613525C4310900BFDB80 /* HapticsSettings.swift */,
);
path = Models;
sourceTree = "<group>";
};
84E372B424FD0AD500AFB355 /* View Models */ = {
isa = PBXGroup;
children = (
841D04D225064A8800786074 /* WorkoutPickerViewModel.swift */,
841D04D225064A8800786074 /* ExercisePickerViewModel.swift */,
);
path = "View Models";
sourceTree = "<group>";
Expand Down Expand Up @@ -546,6 +549,7 @@
84E3729624FD0A5D00AFB355 /* HostingController.swift in Sources */,
849C7325255EA64400C28F96 /* LabelWithIndicator.swift in Sources */,
849458F12503B86200C361F7 /* SpinnerRotationModifier.swift in Sources */,
8404613625C4310900BFDB80 /* HapticsSettings.swift in Sources */,
843F33EE24FFB2E500919AD5 /* Color-extension.swift in Sources */,
84C1FA7C25497CD20008CE09 /* WorkoutTracker.swift in Sources */,
848642F52500FA3E00E3ED9B /* SpinnerSlice.swift in Sources */,
Expand All @@ -564,7 +568,7 @@
84E9C0ED25542309002D461B /* GraphBackgroundSegment.swift in Sources */,
84EFD6792508E32500806EF4 /* BodyPartSelections.swift in Sources */,
84C1FA7425497A050008CE09 /* WorkoutPagingView.swift in Sources */,
841D04D325064A8800786074 /* WorkoutPickerViewModel.swift in Sources */,
841D04D325064A8800786074 /* ExercisePickerViewModel.swift in Sources */,
84E9C0E8255422DE002D461B /* Double-extension.swift in Sources */,
84C10C1F2588D3B500111502 /* CannotSpinView.swift in Sources */,
848364E82565438800B357FA /* InstructionsView.swift in Sources */,
Expand Down

0 comments on commit 6219d21

Please sign in to comment.