From 25e2163a1544a07fb9c8c327f8fae11f10a41d87 Mon Sep 17 00:00:00 2001 From: Charlie Hewitt Date: Sun, 10 Dec 2017 16:34:39 +0000 Subject: [PATCH] Add app --- .../aff-song.xcodeproj/project.pbxproj | 82 ++-- .../UserInterfaceState.xcuserstate | Bin 33533 -> 47801 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 18 + .../aff-song/AffUIViewController.swift | 22 + app/aff-song/aff-song/AppDelegate.swift | 26 +- .../AppIcon.appiconset/Contents.json | 6 +- .../AppIcon.appiconset/icon-1.png | Bin 0 -> 19456 bytes .../AppIcon.appiconset/icon.png | Bin 0 -> 18890 bytes .../aff-song/Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 12 +- .../aff-song/Base.lproj/Main.storyboard | 423 ++++++++++++++-- app/aff-song/aff-song/BorderedImageView.swift | 18 + app/aff-song/aff-song/HCSStarRatingView.h | 51 ++ app/aff-song/aff-song/HCSStarRatingView.m | 463 ++++++++++++++++++ app/aff-song/aff-song/Info.plist | 2 + .../aff-song/ResultsViewController.swift | 72 ++- app/aff-song/aff-song/RoundedUIButton.swift | 16 + app/aff-song/aff-song/SlideHorSegue.swift | 30 ++ .../aff-song/USAnnotateViewController.swift | 38 ++ .../aff-song/USEmojiViewController.swift | 161 ++++++ .../aff-song/USFinishViewController.swift | 17 + .../aff-song/USResultsViewController.swift | 31 ++ .../aff-song/USStartViewController.swift | 16 + app/aff-song/aff-song/ViewController.swift | 50 +- .../aff-song/aff-song-Bridging-Header.h | 5 + app/aff-song/aff-songTests/Info.plist | 22 - .../aff-songTests/aff_songTests.swift | 36 -- app/aff-song/aff-songUITests/Info.plist | 22 - .../aff-songUITests/aff_songUITests.swift | 36 -- icon.png | Bin 0 -> 19456 bytes icon.psd | Bin 0 -> 44245 bytes net.py | 17 +- 32 files changed, 1445 insertions(+), 253 deletions(-) create mode 100644 app/aff-song/aff-song/AffUIViewController.swift create mode 100644 app/aff-song/aff-song/Assets.xcassets/AppIcon.appiconset/icon-1.png create mode 100644 app/aff-song/aff-song/Assets.xcassets/AppIcon.appiconset/icon.png create mode 100644 app/aff-song/aff-song/Assets.xcassets/Contents.json create mode 100644 app/aff-song/aff-song/BorderedImageView.swift create mode 100755 app/aff-song/aff-song/HCSStarRatingView.h create mode 100755 app/aff-song/aff-song/HCSStarRatingView.m create mode 100644 app/aff-song/aff-song/RoundedUIButton.swift create mode 100644 app/aff-song/aff-song/SlideHorSegue.swift create mode 100644 app/aff-song/aff-song/USAnnotateViewController.swift create mode 100644 app/aff-song/aff-song/USEmojiViewController.swift create mode 100644 app/aff-song/aff-song/USFinishViewController.swift create mode 100644 app/aff-song/aff-song/USResultsViewController.swift create mode 100644 app/aff-song/aff-song/USStartViewController.swift create mode 100644 app/aff-song/aff-song/aff-song-Bridging-Header.h delete mode 100644 app/aff-song/aff-songTests/Info.plist delete mode 100644 app/aff-song/aff-songTests/aff_songTests.swift delete mode 100644 app/aff-song/aff-songUITests/Info.plist delete mode 100644 app/aff-song/aff-songUITests/aff_songUITests.swift create mode 100644 icon.png create mode 100644 icon.psd diff --git a/app/aff-song/aff-song.xcodeproj/project.pbxproj b/app/aff-song/aff-song.xcodeproj/project.pbxproj index 9b0ac4a..2ac1492 100644 --- a/app/aff-song/aff-song.xcodeproj/project.pbxproj +++ b/app/aff-song/aff-song.xcodeproj/project.pbxproj @@ -7,14 +7,22 @@ objects = { /* Begin PBXBuildFile section */ + CB0DAF1C1FDD348C004668A1 /* AffUIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0DAF1B1FDD348C004668A1 /* AffUIViewController.swift */; }; + CB0DAF1E1FDD354D004668A1 /* RoundedUIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0DAF1D1FDD354D004668A1 /* RoundedUIButton.swift */; }; + CB0DAF201FDD554C004668A1 /* USResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0DAF1F1FDD554C004668A1 /* USResultsViewController.swift */; }; + CB0DAF221FDD5C63004668A1 /* USEmojiViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0DAF211FDD5C63004668A1 /* USEmojiViewController.swift */; }; + CB0DAF241FDD6352004668A1 /* USAnnotateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0DAF231FDD6352004668A1 /* USAnnotateViewController.swift */; }; + CB0DAF281FDD6646004668A1 /* SlideHorSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0DAF271FDD6646004668A1 /* SlideHorSegue.swift */; }; + CB0DAF2C1FDD685E004668A1 /* USStartViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0DAF2B1FDD685E004668A1 /* USStartViewController.swift */; }; + CB0DAF2E1FDD7497004668A1 /* USFinishViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0DAF2D1FDD7497004668A1 /* USFinishViewController.swift */; }; + CB0DAF301FDD9597004668A1 /* BorderedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0DAF2F1FDD9597004668A1 /* BorderedImageView.swift */; }; + CB418F5B1FDC563800E48EF3 /* HCSStarRatingView.m in Sources */ = {isa = PBXBuildFile; fileRef = CB418F591FDC563700E48EF3 /* HCSStarRatingView.m */; }; CB6ADA771FDB0E3900D36D22 /* ResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB6ADA761FDB0E3900D36D22 /* ResultsViewController.swift */; }; CBB8703C1FDAA373004557D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB8703B1FDAA373004557D9 /* AppDelegate.swift */; }; CBB8703E1FDAA373004557D9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB8703D1FDAA373004557D9 /* ViewController.swift */; }; CBB870411FDAA373004557D9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CBB8703F1FDAA373004557D9 /* Main.storyboard */; }; CBB870431FDAA373004557D9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CBB870421FDAA373004557D9 /* Assets.xcassets */; }; CBB870461FDAA373004557D9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CBB870441FDAA373004557D9 /* LaunchScreen.storyboard */; }; - CBB870511FDAA373004557D9 /* aff_songTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB870501FDAA373004557D9 /* aff_songTests.swift */; }; - CBB8705C1FDAA373004557D9 /* aff_songUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBB8705B1FDAA373004557D9 /* aff_songUITests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -35,6 +43,18 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + CB0DAF1B1FDD348C004668A1 /* AffUIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AffUIViewController.swift; sourceTree = ""; }; + CB0DAF1D1FDD354D004668A1 /* RoundedUIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedUIButton.swift; sourceTree = ""; }; + CB0DAF1F1FDD554C004668A1 /* USResultsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = USResultsViewController.swift; sourceTree = ""; }; + CB0DAF211FDD5C63004668A1 /* USEmojiViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = USEmojiViewController.swift; sourceTree = ""; }; + CB0DAF231FDD6352004668A1 /* USAnnotateViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = USAnnotateViewController.swift; sourceTree = ""; }; + CB0DAF271FDD6646004668A1 /* SlideHorSegue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlideHorSegue.swift; sourceTree = ""; }; + CB0DAF2B1FDD685E004668A1 /* USStartViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = USStartViewController.swift; sourceTree = ""; }; + CB0DAF2D1FDD7497004668A1 /* USFinishViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = USFinishViewController.swift; sourceTree = ""; }; + CB0DAF2F1FDD9597004668A1 /* BorderedImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BorderedImageView.swift; sourceTree = ""; }; + CB418F581FDC563500E48EF3 /* aff-song-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "aff-song-Bridging-Header.h"; sourceTree = ""; }; + CB418F591FDC563700E48EF3 /* HCSStarRatingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HCSStarRatingView.m; sourceTree = ""; }; + CB418F5A1FDC563800E48EF3 /* HCSStarRatingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HCSStarRatingView.h; sourceTree = ""; }; CB6ADA761FDB0E3900D36D22 /* ResultsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultsViewController.swift; sourceTree = ""; }; CBB870381FDAA373004557D9 /* aff-song.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "aff-song.app"; sourceTree = BUILT_PRODUCTS_DIR; }; CBB8703B1FDAA373004557D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -44,11 +64,7 @@ CBB870451FDAA373004557D9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; CBB870471FDAA373004557D9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CBB8704C1FDAA373004557D9 /* aff-songTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "aff-songTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - CBB870501FDAA373004557D9 /* aff_songTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = aff_songTests.swift; sourceTree = ""; }; - CBB870521FDAA373004557D9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CBB870571FDAA373004557D9 /* aff-songUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "aff-songUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - CBB8705B1FDAA373004557D9 /* aff_songUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = aff_songUITests.swift; sourceTree = ""; }; - CBB8705D1FDAA373004557D9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -80,8 +96,6 @@ isa = PBXGroup; children = ( CBB8703A1FDAA373004557D9 /* aff-song */, - CBB8704F1FDAA373004557D9 /* aff-songTests */, - CBB8705A1FDAA373004557D9 /* aff-songUITests */, CBB870391FDAA373004557D9 /* Products */, ); sourceTree = ""; @@ -99,6 +113,8 @@ CBB8703A1FDAA373004557D9 /* aff-song */ = { isa = PBXGroup; children = ( + CB418F5A1FDC563800E48EF3 /* HCSStarRatingView.h */, + CB418F591FDC563700E48EF3 /* HCSStarRatingView.m */, CB6ADA761FDB0E3900D36D22 /* ResultsViewController.swift */, CBB8703B1FDAA373004557D9 /* AppDelegate.swift */, CBB8703D1FDAA373004557D9 /* ViewController.swift */, @@ -106,28 +122,20 @@ CBB870421FDAA373004557D9 /* Assets.xcassets */, CBB870441FDAA373004557D9 /* LaunchScreen.storyboard */, CBB870471FDAA373004557D9 /* Info.plist */, + CB418F581FDC563500E48EF3 /* aff-song-Bridging-Header.h */, + CB0DAF1B1FDD348C004668A1 /* AffUIViewController.swift */, + CB0DAF1D1FDD354D004668A1 /* RoundedUIButton.swift */, + CB0DAF1F1FDD554C004668A1 /* USResultsViewController.swift */, + CB0DAF211FDD5C63004668A1 /* USEmojiViewController.swift */, + CB0DAF231FDD6352004668A1 /* USAnnotateViewController.swift */, + CB0DAF271FDD6646004668A1 /* SlideHorSegue.swift */, + CB0DAF2B1FDD685E004668A1 /* USStartViewController.swift */, + CB0DAF2D1FDD7497004668A1 /* USFinishViewController.swift */, + CB0DAF2F1FDD9597004668A1 /* BorderedImageView.swift */, ); path = "aff-song"; sourceTree = ""; }; - CBB8704F1FDAA373004557D9 /* aff-songTests */ = { - isa = PBXGroup; - children = ( - CBB870501FDAA373004557D9 /* aff_songTests.swift */, - CBB870521FDAA373004557D9 /* Info.plist */, - ); - path = "aff-songTests"; - sourceTree = ""; - }; - CBB8705A1FDAA373004557D9 /* aff-songUITests */ = { - isa = PBXGroup; - children = ( - CBB8705B1FDAA373004557D9 /* aff_songUITests.swift */, - CBB8705D1FDAA373004557D9 /* Info.plist */, - ); - path = "aff-songUITests"; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -196,6 +204,7 @@ TargetAttributes = { CBB870371FDAA373004557D9 = { CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 0920; ProvisioningStyle = Automatic; }; CBB8704B1FDAA373004557D9 = { @@ -262,9 +271,19 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CB0DAF301FDD9597004668A1 /* BorderedImageView.swift in Sources */, CBB8703E1FDAA373004557D9 /* ViewController.swift in Sources */, + CB0DAF221FDD5C63004668A1 /* USEmojiViewController.swift in Sources */, CBB8703C1FDAA373004557D9 /* AppDelegate.swift in Sources */, + CB0DAF281FDD6646004668A1 /* SlideHorSegue.swift in Sources */, + CB0DAF241FDD6352004668A1 /* USAnnotateViewController.swift in Sources */, + CB0DAF1C1FDD348C004668A1 /* AffUIViewController.swift in Sources */, + CB418F5B1FDC563800E48EF3 /* HCSStarRatingView.m in Sources */, CB6ADA771FDB0E3900D36D22 /* ResultsViewController.swift in Sources */, + CB0DAF201FDD554C004668A1 /* USResultsViewController.swift in Sources */, + CB0DAF1E1FDD354D004668A1 /* RoundedUIButton.swift in Sources */, + CB0DAF2C1FDD685E004668A1 /* USStartViewController.swift in Sources */, + CB0DAF2E1FDD7497004668A1 /* USFinishViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -272,7 +291,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - CBB870511FDAA373004557D9 /* aff_songTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -280,7 +298,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - CBB8705C1FDAA373004557D9 /* aff_songUITests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -430,13 +447,16 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = T386P97R96; INFOPLIST_FILE = "aff-song/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "me.chewitt.aff-song"; + PRODUCT_BUNDLE_IDENTIFIER = me.chewitt.affectone; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "aff-song/aff-song-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -446,13 +466,15 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = T386P97R96; INFOPLIST_FILE = "aff-song/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "me.chewitt.aff-song"; + PRODUCT_BUNDLE_IDENTIFIER = me.chewitt.affectone; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "aff-song/aff-song-Bridging-Header.h"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/app/aff-song/aff-song.xcodeproj/project.xcworkspace/xcuserdata/charlie.xcuserdatad/UserInterfaceState.xcuserstate b/app/aff-song/aff-song.xcodeproj/project.xcworkspace/xcuserdata/charlie.xcuserdatad/UserInterfaceState.xcuserstate index a0350d1525e1d59548526998aa68ee15f3bdd64a..ac45b5561c2408f332c833cf28f161cd68cb0cd0 100644 GIT binary patch delta 24810 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 delta 17058 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 diff --git a/app/aff-song/aff-song.xcodeproj/xcuserdata/charlie.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/app/aff-song/aff-song.xcodeproj/xcuserdata/charlie.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index fe2b454..8132c69 100644 --- a/app/aff-song/aff-song.xcodeproj/xcuserdata/charlie.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/app/aff-song/aff-song.xcodeproj/xcuserdata/charlie.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -2,4 +2,22 @@ + + + + + + diff --git a/app/aff-song/aff-song/AffUIViewController.swift b/app/aff-song/aff-song/AffUIViewController.swift new file mode 100644 index 0000000..df0d6a5 --- /dev/null +++ b/app/aff-song/aff-song/AffUIViewController.swift @@ -0,0 +1,22 @@ +// +// AffUIViewController.swift +// aff-song +// +// Created by Charlie Hewitt on 10/12/2017. +// Copyright © 2017 Charlie Hewitt. All rights reserved. +// + +import Foundation + +class AffUIViewController: UIViewController { + + override func viewDidLoad() { + let gradient = CAGradientLayer() + gradient.frame = view.bounds + let c1 = UIColor(red: 252.0/255.0, green: 49.0/255.0, blue: 89.0/255.0, alpha: 1.0) + let c2 = UIColor(red: 252.0/255.0, green: 45.0/255.0, blue: 119.0/255.0, alpha: 1.0) + gradient.colors = [c1.cgColor, c2.cgColor] + view.layer.insertSublayer(gradient, at: 0) + } + +} diff --git a/app/aff-song/aff-song/AppDelegate.swift b/app/aff-song/aff-song/AppDelegate.swift index 6ca2cf6..2163337 100644 --- a/app/aff-song/aff-song/AppDelegate.swift +++ b/app/aff-song/aff-song/AppDelegate.swift @@ -13,10 +13,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. - application.statusBarStyle = .lightContent + refreshAuth() return true } @@ -41,7 +40,26 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } - - + + func refreshAuth() { + // get a new auth key from spotify + let url: String = "https://accounts.spotify.com/api/token" + var request: URLRequest = URLRequest(url: URL(string: url)!) + let bodyData = "grant_type=refresh_token&client_id=75f91608ce154091a1f419be415cbdda&client_secret=a6609b179eb140b086c2f6cc2c35adf4&refresh_token=AQBeDOqXW6kerokqh6WbEexkOQH5FtWGfDvw2DLePMofXTAVOUGsygF5iWYx0jtCjBUFO31qslCGNcRNh6vXGv9wxxbEuyNyWFy-t1YuYON2UD_ySlzSCcjAWsI2YiKzFAc" + request.httpBody = bodyData.data(using: String.Encoding.utf8); + request.httpMethod = "POST" + + let session = URLSession.shared + session.dataTask(with: request) {data, response, err in + do { + let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any] + if let authKey = json["access_token"] { + UserDefaults.standard.set(authKey, forKey: "SpotifyAuthToken") + } + } catch let error { + print(error.localizedDescription) + } + }.resume() + } } diff --git a/app/aff-song/aff-song/Assets.xcassets/AppIcon.appiconset/Contents.json b/app/aff-song/aff-song/Assets.xcassets/AppIcon.appiconset/Contents.json index d8db8d6..7e462ea 100644 --- a/app/aff-song/aff-song/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/app/aff-song/aff-song/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -31,13 +31,15 @@ "scale" : "3x" }, { - "idiom" : "iphone", "size" : "60x60", + "idiom" : "iphone", + "filename" : "icon.png", "scale" : "2x" }, { - "idiom" : "iphone", "size" : "60x60", + "idiom" : "iphone", + "filename" : "icon-1.png", "scale" : "3x" }, { diff --git a/app/aff-song/aff-song/Assets.xcassets/AppIcon.appiconset/icon-1.png b/app/aff-song/aff-song/Assets.xcassets/AppIcon.appiconset/icon-1.png new file mode 100644 index 0000000000000000000000000000000000000000..fd83f3bfc6fd1dc260b00901063af53af59817f0 GIT binary patch literal 19456 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 literal 0 HcmV?d00001 diff --git a/app/aff-song/aff-song/Assets.xcassets/AppIcon.appiconset/icon.png b/app/aff-song/aff-song/Assets.xcassets/AppIcon.appiconset/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b8bd93b8a6a15e1d55aadbb024faf5383f0c52f3 GIT binary patch literal 18890 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 literal 0 HcmV?d00001 diff --git a/app/aff-song/aff-song/Assets.xcassets/Contents.json b/app/aff-song/aff-song/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/app/aff-song/aff-song/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/app/aff-song/aff-song/Base.lproj/LaunchScreen.storyboard b/app/aff-song/aff-song/Base.lproj/LaunchScreen.storyboard index f83f6fd..84bb962 100644 --- a/app/aff-song/aff-song/Base.lproj/LaunchScreen.storyboard +++ b/app/aff-song/aff-song/Base.lproj/LaunchScreen.storyboard @@ -1,7 +1,11 @@ - - + + + + + - + + @@ -13,7 +17,7 @@ - + diff --git a/app/aff-song/aff-song/Base.lproj/Main.storyboard b/app/aff-song/aff-song/Base.lproj/Main.storyboard index be38f6c..c48689b 100644 --- a/app/aff-song/aff-song/Base.lproj/Main.storyboard +++ b/app/aff-song/aff-song/Base.lproj/Main.storyboard @@ -6,6 +6,7 @@ + @@ -18,8 +19,8 @@ - - + + + - + @@ -75,34 +87,15 @@ - - - - - - - - - - + + - + - + @@ -112,21 +105,40 @@ + + + + + + + - + - + + - - + + + + - - + + - @@ -135,5 +147,340 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/aff-song/aff-song/BorderedImageView.swift b/app/aff-song/aff-song/BorderedImageView.swift new file mode 100644 index 0000000..3e7b07b --- /dev/null +++ b/app/aff-song/aff-song/BorderedImageView.swift @@ -0,0 +1,18 @@ +// +// BorderedImageView.swift +// aff-song +// +// Created by Charlie Hewitt on 10/12/2017. +// Copyright © 2017 Charlie Hewitt. All rights reserved. +// + +import Foundation + +class BorderedImageView : UIImageView { + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.layer.borderWidth = 1.0 + self.layer.borderColor = UIColor.white.cgColor + } +} diff --git a/app/aff-song/aff-song/HCSStarRatingView.h b/app/aff-song/aff-song/HCSStarRatingView.h new file mode 100755 index 0000000..04e483b --- /dev/null +++ b/app/aff-song/aff-song/HCSStarRatingView.h @@ -0,0 +1,51 @@ +// HCSStarRatingView.h +// +// Copyright (c) 2015 Hugo Sousa +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +@import UIKit; + +typedef BOOL(^HCSStarRatingViewShouldBeginGestureRecognizerBlock)(UIGestureRecognizer *gestureRecognizer); + +IB_DESIGNABLE +@interface HCSStarRatingView : UIControl +@property (nonatomic) IBInspectable NSUInteger maximumValue; +@property (nonatomic) IBInspectable CGFloat minimumValue; +@property (nonatomic) IBInspectable CGFloat value; +@property (nonatomic) IBInspectable CGFloat spacing; +@property (nonatomic) IBInspectable BOOL allowsHalfStars; +@property (nonatomic) IBInspectable BOOL accurateHalfStars; +@property (nonatomic) IBInspectable BOOL continuous; + +@property (nonatomic) BOOL shouldBecomeFirstResponder; + +// Optional: if `nil` method will return `NO`. +@property (nonatomic, copy) HCSStarRatingViewShouldBeginGestureRecognizerBlock shouldBeginGestureRecognizerBlock; + +@property (nonatomic, strong) IBInspectable UIColor *starBorderColor; +@property (nonatomic) IBInspectable CGFloat starBorderWidth; + +@property (nonatomic, strong) IBInspectable UIColor *emptyStarColor; + +@property (nonatomic, strong) IBInspectable UIImage *emptyStarImage; +@property (nonatomic, strong) IBInspectable UIImage *halfStarImage; +@property (nonatomic, strong) IBInspectable UIImage *filledStarImage; +@end + diff --git a/app/aff-song/aff-song/HCSStarRatingView.m b/app/aff-song/aff-song/HCSStarRatingView.m new file mode 100755 index 0000000..4e9ad8c --- /dev/null +++ b/app/aff-song/aff-song/HCSStarRatingView.m @@ -0,0 +1,463 @@ +// HCSStarRatingView.m +// +// Copyright (c) 2015 Hugo Sousa +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "HCSStarRatingView.h" + +@interface HCSStarRatingView () +@property (nonatomic, readonly) BOOL shouldUseImages; +@end + +@implementation HCSStarRatingView { + CGFloat _minimumValue; + NSUInteger _maximumValue; + CGFloat _value; + UIColor *_starBorderColor; +} + +@dynamic minimumValue; +@dynamic maximumValue; +@dynamic value; +@dynamic shouldUseImages; +@dynamic starBorderColor; + +#pragma mark - Initialization + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self _customInit]; + } + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self _customInit]; + } + return self; +} + +- (void)_customInit { + self.exclusiveTouch = YES; + _minimumValue = 0; + _maximumValue = 5; + _value = 0; + _spacing = 5.f; + _continuous = YES; + _starBorderWidth = 1.0f; + _emptyStarColor = [UIColor clearColor]; + + [self _updateAppearanceForState:self.enabled]; +} + +- (void)setNeedsLayout { + [super setNeedsLayout]; + [self setNeedsDisplay]; +} + +#pragma mark - Properties + +- (UIColor *)backgroundColor { + if ([super backgroundColor]) { + return [super backgroundColor]; + } else { + return self.isOpaque ? [UIColor whiteColor] : [UIColor clearColor]; + }; +} + +- (CGFloat)minimumValue { + return MAX(_minimumValue, 0); +} + +- (void)setMinimumValue:(CGFloat)minimumValue { + if (_minimumValue != minimumValue) { + _minimumValue = minimumValue; + [self setNeedsDisplay]; + } +} + +- (NSUInteger)maximumValue { + return MAX(_minimumValue, _maximumValue); +} + +- (void)setMaximumValue:(NSUInteger)maximumValue { + if (_maximumValue != maximumValue) { + _maximumValue = maximumValue; + [self setNeedsDisplay]; + [self invalidateIntrinsicContentSize]; + } +} + +- (CGFloat)value { + return MIN(MAX(_value, _minimumValue), _maximumValue); +} + +- (void)setValue:(CGFloat)value { + [self setValue:value sendValueChangedAction:NO]; +} + +- (void)setValue:(CGFloat)value sendValueChangedAction:(BOOL)sendAction { + [self willChangeValueForKey:NSStringFromSelector(@selector(value))]; + if (_value != value && value >= _minimumValue && value <= _maximumValue) { + _value = value; + if (sendAction) [self sendActionsForControlEvents:UIControlEventValueChanged]; + [self setNeedsDisplay]; + } + [self didChangeValueForKey:NSStringFromSelector(@selector(value))]; +} + +- (void)setSpacing:(CGFloat)spacing { + _spacing = MAX(spacing, 0); + [self setNeedsDisplay]; +} + +- (void)setAllowsHalfStars:(BOOL)allowsHalfStars { + if (_allowsHalfStars != allowsHalfStars) { + _allowsHalfStars = allowsHalfStars; + [self setNeedsDisplay]; + } +} + +- (void)setAccurateHalfStars:(BOOL)accurateHalfStars { + if (_accurateHalfStars != accurateHalfStars) { + _accurateHalfStars = accurateHalfStars; + [self setNeedsDisplay]; + } +} + +- (void)setEmptyStarImage:(UIImage *)emptyStarImage { + if (_emptyStarImage != emptyStarImage) { + _emptyStarImage = emptyStarImage; + [self setNeedsDisplay]; + } +} + +- (void)setHalfStarImage:(UIImage *)halfStarImage { + if (_halfStarImage != halfStarImage) { + _halfStarImage = halfStarImage; + [self setNeedsDisplay]; + } +} + +- (void)setFilledStarImage:(UIImage *)filledStarImage { + if (_filledStarImage != filledStarImage) { + _filledStarImage = filledStarImage; + [self setNeedsDisplay]; + } +} + +- (void)setEmptyStarColor:(UIColor *)emptyStarColor { + if (_emptyStarColor != emptyStarColor) { + _emptyStarColor = emptyStarColor; + [self setNeedsDisplay]; + } +} + +- (void)setStarBorderColor:(UIColor *)starBorderColor { + if (_starBorderColor != starBorderColor) { + _starBorderColor = starBorderColor; + [self setNeedsDisplay]; + } +} + +- (UIColor *)starBorderColor { + if (_starBorderColor == nil) { + return self.tintColor; + } else { + return _starBorderColor; + } +} + +- (void)setStarBorderWidth:(CGFloat)starBorderWidth { + _starBorderWidth = MAX(0, starBorderWidth); + [self setNeedsDisplay]; +} + + +- (BOOL)shouldUseImages { + return (self.emptyStarImage!=nil && self.filledStarImage!=nil); +} + +#pragma mark - State + +- (void)setEnabled:(BOOL)enabled +{ + [self _updateAppearanceForState:enabled]; + [super setEnabled:enabled]; +} + +- (void)_updateAppearanceForState:(BOOL)enabled +{ + self.alpha = enabled ? 1.f : .5f; +} + +#pragma mark - Image Drawing + +- (void)_drawStarImageWithFrame:(CGRect)frame tintColor:(UIColor*)tintColor highlighted:(BOOL)highlighted { + UIImage *image = highlighted ? self.filledStarImage : self.emptyStarImage; + [self _drawImage:image frame:frame tintColor:tintColor]; +} + +- (void)_drawHalfStarImageWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor { + [self _drawAccurateHalfStarImageWithFrame:frame tintColor:tintColor progress:.5f]; +} + +- (void)_drawAccurateHalfStarImageWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor progress:(CGFloat)progress { + UIImage *image = self.halfStarImage; + if (image == nil) { + // first draw star outline + [self _drawStarImageWithFrame:frame tintColor:tintColor highlighted:NO]; + + image = self.filledStarImage; + CGRect imageFrame = CGRectMake(0, 0, image.size.width * image.scale * progress, image.size.height * image.scale); + frame.size.width *= progress; + CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, imageFrame); + UIImage *halfImage = [UIImage imageWithCGImage:imageRef scale:image.scale orientation:image.imageOrientation]; + image = [halfImage imageWithRenderingMode:image.renderingMode]; + CGImageRelease(imageRef); + } + [self _drawImage:image frame:frame tintColor:tintColor]; +} + +- (void)_drawImage:(UIImage *)image frame:(CGRect)frame tintColor:(UIColor *)tintColor { + if (image.renderingMode == UIImageRenderingModeAlwaysTemplate) { + [tintColor setFill]; + } + [image drawInRect:frame]; +} + +#pragma mark - Shape Drawing + +- (void)_drawStarShapeWithFrame:(CGRect)frame tintColor:(UIColor*)tintColor highlighted:(BOOL)highlighted { + [self _drawAccurateHalfStarShapeWithFrame:frame tintColor:tintColor progress:highlighted ? 1.f : 0.f]; +} + +- (void)_drawHalfStarShapeWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor { + [self _drawAccurateHalfStarShapeWithFrame:frame tintColor:tintColor progress:.5f]; +} + +- (void)_drawAccurateHalfStarShapeWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor progress:(CGFloat)progress { + UIBezierPath* starShapePath = UIBezierPath.bezierPath; + [starShapePath moveToPoint: CGPointMake(CGRectGetMinX(frame) + 0.62723 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.37309 * CGRectGetHeight(frame))]; + [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.50000 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.02500 * CGRectGetHeight(frame))]; + [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.37292 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.37309 * CGRectGetHeight(frame))]; + [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.02500 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.39112 * CGRectGetHeight(frame))]; + [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.30504 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.62908 * CGRectGetHeight(frame))]; + [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.20642 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.97500 * CGRectGetHeight(frame))]; + [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.50000 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.78265 * CGRectGetHeight(frame))]; + [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.79358 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.97500 * CGRectGetHeight(frame))]; + [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.69501 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.62908 * CGRectGetHeight(frame))]; + [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.97500 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.39112 * CGRectGetHeight(frame))]; + [starShapePath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 0.62723 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.37309 * CGRectGetHeight(frame))]; + [starShapePath closePath]; + starShapePath.miterLimit = 4; + + CGFloat frameWidth = frame.size.width; + CGRect rightRectOfStar = CGRectMake(frame.origin.x + progress * frameWidth, frame.origin.y, frameWidth - progress * frameWidth, frame.size.height); + UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:CGRectInfinite]; + [clipPath appendPath:[UIBezierPath bezierPathWithRect:rightRectOfStar]]; + clipPath.usesEvenOddFillRule = YES; + + [_emptyStarColor setFill]; + [starShapePath fill]; + + CGContextSaveGState(UIGraphicsGetCurrentContext()); { + [clipPath addClip]; + [tintColor setFill]; + [starShapePath fill]; + } + CGContextRestoreGState(UIGraphicsGetCurrentContext()); + + [self.starBorderColor setStroke]; + starShapePath.lineWidth = _starBorderWidth; + [starShapePath stroke]; +} + +#pragma mark - Drawing + +- (void)drawRect:(CGRect)rect { + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetFillColorWithColor(context, self.backgroundColor.CGColor); + CGContextFillRect(context, rect); + + CGFloat availableWidth = rect.size.width - (_spacing * (_maximumValue - 1)) - 2; + CGFloat cellWidth = (availableWidth / _maximumValue); + CGFloat starSide = (cellWidth <= rect.size.height) ? cellWidth : rect.size.height; + starSide = (self.shouldUseImages) ? starSide : (starSide - _starBorderWidth); + + for (int idx = 0; idx < _maximumValue; idx++) { + CGPoint center = CGPointMake(cellWidth*idx + cellWidth/2 + _spacing*idx + 1, rect.size.height/2); + CGRect frame = CGRectMake(center.x - starSide/2, center.y - starSide/2, starSide, starSide); + BOOL highlighted = (idx+1 <= ceilf(_value)); + if (_allowsHalfStars && highlighted && (idx+1 > _value)) { + if (_accurateHalfStars) { + [self _drawAccurateStarWithFrame:frame tintColor:self.tintColor progress:_value - idx]; + } + else { + [self _drawHalfStarWithFrame:frame tintColor:self.tintColor]; + } + } else { + [self _drawStarWithFrame:frame tintColor:self.tintColor highlighted:highlighted]; + } + } +} + +- (void)_drawStarWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor highlighted:(BOOL)highlighted { + if (self.shouldUseImages) { + [self _drawStarImageWithFrame:frame tintColor:tintColor highlighted:highlighted]; + } else { + [self _drawStarShapeWithFrame:frame tintColor:tintColor highlighted:highlighted]; + } +} + +- (void)_drawHalfStarWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor { + if (self.shouldUseImages) { + [self _drawHalfStarImageWithFrame:frame tintColor:tintColor]; + } else { + [self _drawHalfStarShapeWithFrame:frame tintColor:tintColor]; + } +} +- (void)_drawAccurateStarWithFrame:(CGRect)frame tintColor:(UIColor *)tintColor progress:(CGFloat)progress { + if (self.shouldUseImages) { + [self _drawAccurateHalfStarImageWithFrame:frame tintColor:tintColor progress:progress]; + } else { + [self _drawAccurateHalfStarShapeWithFrame:frame tintColor:tintColor progress:progress]; + } +} +#pragma mark - Touches + +- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { + if (self.isEnabled) { + [super beginTrackingWithTouch:touch withEvent:event]; + if (_shouldBecomeFirstResponder && ![self isFirstResponder]) { + [self becomeFirstResponder]; + } + [self _handleTouch:touch]; + return YES; + } else { + return NO; + } +} + +- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { + if (self.isEnabled) { + [super continueTrackingWithTouch:touch withEvent:event]; + [self _handleTouch:touch]; + return YES; + } else { + return NO; + } +} + +- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { + [super endTrackingWithTouch:touch withEvent:event]; + if (_shouldBecomeFirstResponder && [self isFirstResponder]) { + [self resignFirstResponder]; + } + [self _handleTouch:touch]; + if (!_continuous) { + [self sendActionsForControlEvents:UIControlEventValueChanged]; + } +} + +- (void)cancelTrackingWithEvent:(UIEvent *)event { + [super cancelTrackingWithEvent:event]; + if (_shouldBecomeFirstResponder && [self isFirstResponder]) { + [self resignFirstResponder]; + } +} + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { + if ([gestureRecognizer.view isEqual:self]) { + return !self.isUserInteractionEnabled; + } + return self.shouldBeginGestureRecognizerBlock ? self.shouldBeginGestureRecognizerBlock(gestureRecognizer) : NO; +} + +- (void)_handleTouch:(UITouch *)touch { + CGFloat cellWidth = self.bounds.size.width / _maximumValue; + CGPoint location = [touch locationInView:self]; + CGFloat value = location.x / cellWidth; + if (_allowsHalfStars) { + if (_accurateHalfStars) { + value = value; + } + else { + if (value+.5f < ceilf(value)) { + value = floor(value)+.5f; + } else { + value = ceilf(value); + } + } + } else { + value = ceilf(value); + } + [self setValue:value sendValueChangedAction:_continuous]; +} + +#pragma mark - First responder + +- (BOOL)canBecomeFirstResponder { + return _shouldBecomeFirstResponder; +} + +#pragma mark - Intrinsic Content Size + +- (CGSize)intrinsicContentSize { + CGFloat height = 44.f; + return CGSizeMake(_maximumValue * height + (_maximumValue-1) * _spacing, height); +} + +#pragma mark - Accessibility + +- (BOOL)isAccessibilityElement { + return YES; +} + +- (NSString *)accessibilityLabel { + return [super accessibilityLabel] ?: NSLocalizedString(@"Rating", @"Accessibility label for star rating control."); +} + +- (UIAccessibilityTraits)accessibilityTraits { + return ([super accessibilityTraits] | UIAccessibilityTraitAdjustable); +} + +- (NSString *)accessibilityValue { + return [@(self.value) description]; +} + +- (BOOL)accessibilityActivate { + return YES; +} + +- (void)accessibilityIncrement { + CGFloat value = self.value + (self.allowsHalfStars ? .5f : 1.f); + [self setValue:value sendValueChangedAction:YES]; +} + +- (void)accessibilityDecrement { + CGFloat value = self.value - (self.allowsHalfStars ? .5f : 1.f); + [self setValue:value sendValueChangedAction:YES]; +} + +@end diff --git a/app/aff-song/aff-song/Info.plist b/app/aff-song/aff-song/Info.plist index a2b4f95..fdb42dc 100644 --- a/app/aff-song/aff-song/Info.plist +++ b/app/aff-song/aff-song/Info.plist @@ -32,6 +32,8 @@ armv7 + UIStatusBarStyle + UIStatusBarStyleLightContent UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/app/aff-song/aff-song/ResultsViewController.swift b/app/aff-song/aff-song/ResultsViewController.swift index a237e3d..77d1b9d 100644 --- a/app/aff-song/aff-song/ResultsViewController.swift +++ b/app/aff-song/aff-song/ResultsViewController.swift @@ -8,28 +8,19 @@ import UIKit import CoreML +import Vision -class ResultsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { +class ResultsViewController: AffUIViewController, UITableViewDataSource, UITableViewDelegate { - var spotifyAuthKey : String = "" var faceImage : UIImage? var tracks : [[String]]? @IBOutlet var imageView: UIImageView! - @IBOutlet weak var BackButton: UIButton! @IBOutlet weak var TrackTable: UITableView! override func viewDidLoad() { super.viewDidLoad() - // Do any additional setup after loading the view, typically from a nib. - let gradient = CAGradientLayer() - gradient.frame = view.bounds - let c1 = UIColor(red: 252.0/255.0, green: 49.0/255.0, blue: 89.0/255.0, alpha: 1.0) - let c2 = UIColor(red: 252.0/255.0, green: 45.0/255.0, blue: 119.0/255.0, alpha: 1.0) - gradient.colors = [c1.cgColor, c2.cgColor] - view.layer.insertSublayer(gradient, at: 0) - BackButton.layer.cornerRadius = BackButton.layer.frame.height/2 - let (valence, arousal) = getAffect(image: faceImage) imageView.image = faceImage + let (valence, arousal) = getAffect(image: faceImage) getSongs(valence: valence, arousal: arousal, callback: {tracks in self.tracks = tracks DispatchQueue.main.async { @@ -39,6 +30,7 @@ class ResultsViewController: UIViewController, UITableViewDataSource, UITableVie } @IBAction func backButtonClick(_ sender: Any) { + print("BACK") dismiss(animated: true, completion: nil) } @@ -48,11 +40,14 @@ class ResultsViewController: UIViewController, UITableViewDataSource, UITableVie } func getSongs(valence : Double, arousal: Double, callback: @escaping ([[String]])->Void){ + guard let authKey = UserDefaults.standard.string(forKey: "SpotifyAuthToken") else { + fatalError("Spotify auth failed") + } + let urlPath: String = "https://api.spotify.com/v1/recommendations?seed_genres=pop,rock,dance&valence=\(valence)&danceability=\(arousal)&limit=5&market=GB" - print(urlPath) var request: URLRequest = URLRequest(url: URL(string: urlPath)!) request.httpMethod = "GET" - request.addValue("Bearer " + self.spotifyAuthKey, forHTTPHeaderField: "Authorization") + request.addValue("Bearer " + authKey, forHTTPHeaderField: "Authorization") let session = URLSession.shared session.dataTask(with: request) {data, response, err in @@ -74,6 +69,11 @@ class ResultsViewController: UIViewController, UITableViewDataSource, UITableVie func getAffect(image: UIImage?) -> (Double, Double) { // TODO CNN +// let model = nil +// let pixelBuffer = UIImage(cgImage: image.cgImage).pixelBuffer() +// guard let modelPrediction = try? model.prediction(image: pixelBuffer) else { +// fatalError("Unexpected runtime error.") +// } return (0.5, 0.5) } @@ -97,3 +97,47 @@ class ResultsViewController: UIViewController, UITableViewDataSource, UITableVie UIApplication.shared.openURL(URL(string: self.tracks![indexPath.row][2])!) } } + +extension UIImage { + func pixelBuffer() -> CVPixelBuffer? { + let width = self.size.width + let height = self.size.height + let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, + kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue] as CFDictionary + var pixelBuffer: CVPixelBuffer? + let status = CVPixelBufferCreate(kCFAllocatorDefault, + Int(width), + Int(height), + kCVPixelFormatType_OneComponent8, + attrs, + &pixelBuffer) + + guard let resultPixelBuffer = pixelBuffer, status == kCVReturnSuccess else { + return nil + } + + CVPixelBufferLockBaseAddress(resultPixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) + let pixelData = CVPixelBufferGetBaseAddress(resultPixelBuffer) + + let grayColorSpace = CGColorSpaceCreateDeviceGray() + guard let context = CGContext(data: pixelData, + width: Int(width), + height: Int(height), + bitsPerComponent: 8, + bytesPerRow: CVPixelBufferGetBytesPerRow(resultPixelBuffer), + space: grayColorSpace, + bitmapInfo: CGImageAlphaInfo.none.rawValue) else { + return nil + } + + context.translateBy(x: 0, y: height) + context.scaleBy(x: 1.0, y: -1.0) + + UIGraphicsPushContext(context) + self.draw(in: CGRect(x: 0, y: 0, width: width, height: height)) + UIGraphicsPopContext() + CVPixelBufferUnlockBaseAddress(resultPixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) + + return resultPixelBuffer + } +} diff --git a/app/aff-song/aff-song/RoundedUIButton.swift b/app/aff-song/aff-song/RoundedUIButton.swift new file mode 100644 index 0000000..98fe02f --- /dev/null +++ b/app/aff-song/aff-song/RoundedUIButton.swift @@ -0,0 +1,16 @@ +// +// RoundedUIButton.swift +// aff-song +// +// Created by Charlie Hewitt on 10/12/2017. +// Copyright © 2017 Charlie Hewitt. All rights reserved. +// + +import Foundation + +class RoundedUIButton : UIButton { + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.layer.cornerRadius = self.frame.height/2 + } +} diff --git a/app/aff-song/aff-song/SlideHorSegue.swift b/app/aff-song/aff-song/SlideHorSegue.swift new file mode 100644 index 0000000..f054c53 --- /dev/null +++ b/app/aff-song/aff-song/SlideHorSegue.swift @@ -0,0 +1,30 @@ +// +// SlideHorSegue.swift +// aff-song +// +// Created by Charlie Hewitt on 10/12/2017. +// Copyright © 2017 Charlie Hewitt. All rights reserved. +// + +import UIKit + +class SlideHorSegue: UIStoryboardSegue { + override func perform() { + //set the ViewControllers for the animation + let sourceView = self.source.view as UIView! + let destinationView = self.destination.view as UIView! + let window = UIApplication.shared.delegate?.window! + window?.insertSubview(destinationView!, belowSubview: sourceView!) + destinationView?.center = CGPoint(x: (sourceView?.center.x)! + (destinationView?.frame.width)!, y: (sourceView?.center.y)!) + UIView.animate(withDuration: 0.4, + animations: { + destinationView?.center = CGPoint(x: (sourceView?.center.x)!, y: (sourceView?.center.y)!) + }, completion: { + (value: Bool) in + destinationView?.removeFromSuperview() + if let navController = self.destination.navigationController { + navController.popToViewController(self.destination, animated: false) + } + }) + } +} diff --git a/app/aff-song/aff-song/USAnnotateViewController.swift b/app/aff-song/aff-song/USAnnotateViewController.swift new file mode 100644 index 0000000..65585e1 --- /dev/null +++ b/app/aff-song/aff-song/USAnnotateViewController.swift @@ -0,0 +1,38 @@ +// +// USAnnotateViewController.swift +// aff-song +// +// Created by Charlie Hewitt on 10/12/2017. +// Copyright © 2017 Charlie Hewitt. All rights reserved. +// + +import Foundation + +class USAnnotateViewController : AffUIViewController { + + var emotion: Int? + var faceImage: UIImage? + @IBOutlet weak var ActionButton: RoundedUIButton! + @IBOutlet weak var imageView: UIImageView! + @IBOutlet weak var ValenceSlider: UISlider! + @IBOutlet weak var ArousalSlider: UISlider! + + override func viewDidLoad() { + super.viewDidLoad() + imageView.image = faceImage + if (emotion! > 6) { + ActionButton.titleLabel?.text = "Finish" + } + } + + @IBAction func ButtonClicked(_ sender: Any) { + // TODO save values + prtin(ValenceSlider.value, ArousalSlider.value) + if(emotion! > 6){ + self.performSegue(withIdentifier: "usDoneSegue", sender: self) + } + else { + self.performSegue(withIdentifier: "usNextSegue", sender: self) + } + } +} diff --git a/app/aff-song/aff-song/USEmojiViewController.swift b/app/aff-song/aff-song/USEmojiViewController.swift new file mode 100644 index 0000000..31d0bff --- /dev/null +++ b/app/aff-song/aff-song/USEmojiViewController.swift @@ -0,0 +1,161 @@ +// +// USEmojiViewController.swift +// aff-song +// +// Created by Charlie Hewitt on 10/12/2017. +// Copyright © 2017 Charlie Hewitt. All rights reserved. +// + +import Foundation +import Vision + +class USEmojiViewController : AffUIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { + + var faceImage : UIImage? + var emotion: Int = 0 + @IBOutlet weak var EmotionLabel: UILabel! + @IBOutlet weak var EmojiLabel: UILabel! + + @IBAction func unwindToEmoji(unwindSegue: UIStoryboardSegue) { + emotion += 1 + updateForEmotionChange() + } + + override func viewDidLoad() { + super.viewDidLoad() + updateForEmotionChange() + } + + func updateForEmotionChange() { + var emotionText : String? + var emotionEmoji : String? + switch emotion { + case 0: + emotionText = "Neutral" + emotionEmoji = "😐" + break + case 1: + emotionText = "Happy" + emotionEmoji = "😀" + break + case 2: + emotionText = "Sad" + emotionEmoji = "😟" + break + case 3: + emotionText = "Suprised" + emotionEmoji = "😲" + break + case 4: + emotionText = "Afraid" + emotionEmoji = "😨" + break + case 5: + emotionText = "Disgusted" + emotionEmoji = "😖" + break + case 6: + emotionText = "Angry" + emotionEmoji = "😡" + break + case 7: + emotionText = "Contemptful" + emotionEmoji = "🤨" + break + default: + emotionText = "Error" + emotionEmoji = "🤪" + } + EmotionLabel.text = emotionText + EmojiLabel.text = emotionEmoji + } + + @IBAction func openCameraButton(sender: AnyObject) { + if UIImagePickerController.isSourceTypeAvailable(.camera) { + let imagePicker = UIImagePickerController() + imagePicker.delegate = self + imagePicker.sourceType = .camera + imagePicker.cameraDevice = .front + imagePicker.allowsEditing = false + self.present(imagePicker, animated: true, completion: nil) + } + } + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { + if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage, + let face = detectFaces(image: pickedImage) { + self.faceImage = face + dismiss(animated: true, completion: { + self.performSegue(withIdentifier: "usResultsSegue", sender: self) + }) + } + else { + dismiss(animated: true, completion: nil) + } + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if segue.identifier == "usResultsSegue" { + if let nextViewController = segue.destination as? USResultsViewController { + nextViewController.faceImage = self.faceImage + nextViewController.emotion = self.emotion + } + } + } + + func detectFaces(image: UIImage) -> UIImage? { + let orientation = CGImagePropertyOrientation(rawValue: imageOrientationToExif(image: image))! + let faceDetectionRequest = VNDetectFaceRectanglesRequest() + let myRequestHandler = VNImageRequestHandler(cgImage: image.cgImage!, orientation: orientation, options: [:]) + try! myRequestHandler.perform([faceDetectionRequest]) + guard let results = faceDetectionRequest.results, + let cgImage = image.cgImage, + results.count > 0 else { + return nil + } + for observation in faceDetectionRequest.results as! [VNFaceObservation] { + let box = observation.boundingBox + // iOS is a joke for image handling... + // flip h/w as in portrait but cg image is landscape + let imw = CGFloat(cgImage.height) + let imh = CGFloat(cgImage.width) + let w = box.size.width * imw + let h = box.size.height * imh + let x = box.origin.x * imw + let y = box.origin.y * imh + // and fix for stupid coordinate system inconsistencies + let cropRect = CGRect(x: imh - (y+h), y: imw - (x+w), width: h, height: w) + let croppedCGImage: CGImage = cgImage.cropping(to: cropRect)! + let croppedUIImage: UIImage = UIImage(cgImage: croppedCGImage, scale: image.scale, orientation: image.imageOrientation) + let size = CGSize(width:256,height:256) + UIGraphicsBeginImageContextWithOptions(size, false, image.scale) + croppedUIImage.draw(in: CGRect(origin: CGPoint.zero, size: size)) + let scaledImage = UIGraphicsGetImageFromCurrentImageContext()! + UIGraphicsEndImageContext() + return scaledImage + } + return nil + } + + func imageOrientationToExif(image: UIImage) -> uint { + switch image.imageOrientation { + case .up: + return 1; + case .down: + return 3; + case .left: + return 8; + case .right: + return 6; + case .upMirrored: + return 2; + case .downMirrored: + return 4; + case .leftMirrored: + return 5; + case .rightMirrored: + return 7; + } + } + +} diff --git a/app/aff-song/aff-song/USFinishViewController.swift b/app/aff-song/aff-song/USFinishViewController.swift new file mode 100644 index 0000000..dd7f7cd --- /dev/null +++ b/app/aff-song/aff-song/USFinishViewController.swift @@ -0,0 +1,17 @@ +// +// USFinishViewController.swift +// aff-song +// +// Created by Charlie Hewitt on 10/12/2017. +// Copyright © 2017 Charlie Hewitt. All rights reserved. +// + +import Foundation + +class USFinishViewController : AffUIViewController { + + @IBAction func FinishButtonClicked(_ sender: Any) { +// performSegue(withIdentifier: "returnToFrontSegue", sender: self) + self.navigationController?.dismiss(animated: true, completion: nil) + } +} diff --git a/app/aff-song/aff-song/USResultsViewController.swift b/app/aff-song/aff-song/USResultsViewController.swift new file mode 100644 index 0000000..24d76b0 --- /dev/null +++ b/app/aff-song/aff-song/USResultsViewController.swift @@ -0,0 +1,31 @@ +// +// USResultsViewController.swift +// aff-song +// +// Created by Charlie Hewitt on 10/12/2017. +// Copyright © 2017 Charlie Hewitt. All rights reserved. +// + +import Foundation + +class USResultsViewController: ResultsViewController, UINavigationControllerDelegate { + + var emotion: Int? + @IBOutlet weak var StarRatingView: HCSStarRatingView! + + override func viewDidLoad() { + super.viewDidLoad() + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + if segue.identifier == "usAnnotateSegue" { + // TODO save rating + print(StarRatingView.value) + if let nextViewController = segue.destination as? USAnnotateViewController { + nextViewController.emotion = self.emotion + nextViewController.faceImage = self.faceImage + } + } + } + +} diff --git a/app/aff-song/aff-song/USStartViewController.swift b/app/aff-song/aff-song/USStartViewController.swift new file mode 100644 index 0000000..23f2296 --- /dev/null +++ b/app/aff-song/aff-song/USStartViewController.swift @@ -0,0 +1,16 @@ +// +// USStartViewController.swift +// aff-song +// +// Created by Charlie Hewitt on 10/12/2017. +// Copyright © 2017 Charlie Hewitt. All rights reserved. +// + +import Foundation + +class USStartViewController : AffUIViewController { + + @IBAction func cancelButtonClicked(_ sender: Any) { + navigationController?.dismiss(animated: true, completion: nil) + } +} diff --git a/app/aff-song/aff-song/ViewController.swift b/app/aff-song/aff-song/ViewController.swift index 63c770d..9a7936a 100644 --- a/app/aff-song/aff-song/ViewController.swift +++ b/app/aff-song/aff-song/ViewController.swift @@ -10,29 +10,15 @@ import UIKit import Vision import CoreGraphics -class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { +class ViewController: AffUIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { - var spotifyAuthKey : String = "" var faceImage : UIImage? - @IBOutlet weak var PhotoButton: UIButton! override func viewDidLoad() { super.viewDidLoad() - PhotoButton.layer.cornerRadius = PhotoButton.layer.frame.height/2 - let gradient = CAGradientLayer() - gradient.frame = view.bounds - let c1 = UIColor(red: 252.0/255.0, green: 49.0/255.0, blue: 89.0/255.0, alpha: 1.0) - let c2 = UIColor(red: 252.0/255.0, green: 45.0/255.0, blue: 119.0/255.0, alpha: 1.0) - gradient.colors = [c1.cgColor, c2.cgColor] - view.layer.insertSublayer(gradient, at: 0) - refreshAuth() // Do any additional setup after loading the view, typically from a nib. } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } + @IBAction func openCameraButton(sender: AnyObject) { if UIImagePickerController.isSourceTypeAvailable(.camera) { @@ -46,10 +32,12 @@ class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavig } func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { - if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage { - self.faceImage = detectFaces(image: pickedImage)! - dismiss(animated: true, completion: nil) - performSegue(withIdentifier: "resultsViewSegue", sender: self) + if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage, + let face = detectFaces(image: pickedImage) { + self.faceImage = face + dismiss(animated: true, completion: { + self.performSegue(withIdentifier: "resultsViewSegue", sender: self) + }) } else { dismiss(animated: true, completion: nil) @@ -60,7 +48,6 @@ class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavig if segue.identifier == "resultsViewSegue" { if let imageViewController = segue.destination as? ResultsViewController { imageViewController.faceImage = self.faceImage - imageViewController.spotifyAuthKey = self.spotifyAuthKey } } } @@ -99,27 +86,6 @@ class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavig return nil } - func refreshAuth() { - // get a new auth key from spotify - let url: String = "https://accounts.spotify.com/api/token" - var request: URLRequest = URLRequest(url: URL(string: url)!) - let bodyData = "grant_type=refresh_token&client_id=75f91608ce154091a1f419be415cbdda&client_secret=a6609b179eb140b086c2f6cc2c35adf4&refresh_token=AQBeDOqXW6kerokqh6WbEexkOQH5FtWGfDvw2DLePMofXTAVOUGsygF5iWYx0jtCjBUFO31qslCGNcRNh6vXGv9wxxbEuyNyWFy-t1YuYON2UD_ySlzSCcjAWsI2YiKzFAc" - request.httpBody = bodyData.data(using: String.Encoding.utf8); - request.httpMethod = "POST" - - let session = URLSession.shared - session.dataTask(with: request) {data, response, err in - do { - let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any] - if let authKey = json["access_token"] { - self.spotifyAuthKey = authKey as! String - } - } catch let error { - print(error.localizedDescription) - } - }.resume() - } - func imageOrientationToExif(image: UIImage) -> uint { switch image.imageOrientation { case .up: diff --git a/app/aff-song/aff-song/aff-song-Bridging-Header.h b/app/aff-song/aff-song/aff-song-Bridging-Header.h new file mode 100644 index 0000000..5f0e89a --- /dev/null +++ b/app/aff-song/aff-song/aff-song-Bridging-Header.h @@ -0,0 +1,5 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "HCSStarRatingView.h" diff --git a/app/aff-song/aff-songTests/Info.plist b/app/aff-song/aff-songTests/Info.plist deleted file mode 100644 index 6c40a6c..0000000 --- a/app/aff-song/aff-songTests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/app/aff-song/aff-songTests/aff_songTests.swift b/app/aff-song/aff-songTests/aff_songTests.swift deleted file mode 100644 index 2970f75..0000000 --- a/app/aff-song/aff-songTests/aff_songTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// aff_songTests.swift -// aff-songTests -// -// Created by Charlie Hewitt on 08/12/2017. -// Copyright © 2017 Charlie Hewitt. All rights reserved. -// - -import XCTest -@testable import aff_song - -class aff_songTests: XCTestCase { - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/app/aff-song/aff-songUITests/Info.plist b/app/aff-song/aff-songUITests/Info.plist deleted file mode 100644 index 6c40a6c..0000000 --- a/app/aff-song/aff-songUITests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/app/aff-song/aff-songUITests/aff_songUITests.swift b/app/aff-song/aff-songUITests/aff_songUITests.swift deleted file mode 100644 index 7fef977..0000000 --- a/app/aff-song/aff-songUITests/aff_songUITests.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// aff_songUITests.swift -// aff-songUITests -// -// Created by Charlie Hewitt on 08/12/2017. -// Copyright © 2017 Charlie Hewitt. All rights reserved. -// - -import XCTest - -class aff_songUITests: XCTestCase { - - override func setUp() { - super.setUp() - - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. - XCUIApplication().launch() - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // Use recording to get started writing UI tests. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - -} diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fd83f3bfc6fd1dc260b00901063af53af59817f0 GIT binary patch literal 19456 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 literal 0 HcmV?d00001 diff --git a/icon.psd b/icon.psd new file mode 100644 index 0000000000000000000000000000000000000000..d19c9377ab6953d347368dd4392b6b3f2fdd6db4 GIT binary patch literal 44245 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 P{KJL?>JE{pp=61Si* literal 0 HcmV?d00001 diff --git a/net.py b/net.py index 077932d..1d639c3 100644 --- a/net.py +++ b/net.py @@ -1,10 +1,14 @@ from keras.preprocessing import image from keras.models import Sequential from keras.layers import Conv2D, MaxPooling2D, Activation, Dropout, Flatten, Dense +from keras.utils.np_utils import to_categorical from math import floor import csv import numpy as np +# TODO classifier for emotions too (use to_categorical on labels) +CLASSIFY_OR_REGRESS = 1 + def load_images(paths, labels, batch_size=32): while True: batch_n = 0 @@ -61,9 +65,12 @@ model.add(Dense(1024, activation='relu')) model.add(Dropout(0.4)) model.add(Dense(1024, activation='relu')) model.add(Dropout(0.4)) -model.add(Dense(2, activation='linear')) - -model.compile(loss='mean_squared_error', optimizer='adam') +if CLASSIFY_OR_REGRESS == 0: + model.add(Dense(7, activation='softmax')) + model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) +else: + model.add(Dense(2, activation='linear')) + model.compile(loss='mean_squared_error', optimizer='adam') t_paths, t_labels = load_paths('training.csv') v_paths, v_labels = load_paths('validation.csv') @@ -76,4 +83,8 @@ model.fit_generator( validation_data=load_images(v_paths,v_labels, batch_size), validation_steps=800 // batch_size) +for k in model.layers: + if type(k) is keras.layers.Dropout: + model.layers.remove(k) + model.save_weights('aff_net.h5')