├── .gitignore ├── Binary └── HackathonAlexa.app │ └── Contents │ ├── Frameworks │ ├── GCDWebServers.framework │ │ ├── GCDWebServers │ │ ├── Resources │ │ └── Versions │ │ │ ├── A │ │ │ ├── GCDWebServers │ │ │ ├── Resources │ │ │ │ ├── GCDWebUploader.bundle │ │ │ │ │ ├── Info.plist │ │ │ │ │ ├── css │ │ │ │ │ │ ├── bootstrap-theme.css │ │ │ │ │ │ ├── bootstrap.css │ │ │ │ │ │ ├── index.css │ │ │ │ │ │ └── jquery.fileupload.css │ │ │ │ │ ├── en.lproj │ │ │ │ │ │ └── Localizable.strings │ │ │ │ │ ├── fonts │ │ │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ │ │ └── glyphicons-halflings-regular.woff │ │ │ │ │ ├── index.html │ │ │ │ │ └── js │ │ │ │ │ │ ├── bootstrap.min.js │ │ │ │ │ │ ├── html5shiv.min.js │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ ├── jquery.fileupload.js │ │ │ │ │ │ ├── jquery.iframe-transport.js │ │ │ │ │ │ ├── jquery.jeditable.js │ │ │ │ │ │ ├── jquery.min.js │ │ │ │ │ │ ├── jquery.ui.widget.js │ │ │ │ │ │ ├── respond.min.js │ │ │ │ │ │ └── tmpl.min.js │ │ │ │ └── Info.plist │ │ │ └── _CodeSignature │ │ │ │ └── CodeResources │ │ │ └── Current │ ├── libswiftAVFoundation.dylib │ ├── libswiftAppKit.dylib │ ├── libswiftCore.dylib │ ├── libswiftCoreAudio.dylib │ ├── libswiftCoreData.dylib │ ├── libswiftCoreGraphics.dylib │ ├── libswiftCoreImage.dylib │ ├── libswiftCoreMedia.dylib │ ├── libswiftDarwin.dylib │ ├── libswiftDispatch.dylib │ ├── libswiftFoundation.dylib │ ├── libswiftIOKit.dylib │ └── libswiftObjectiveC.dylib │ ├── Info.plist │ ├── MacOS │ └── HackathonAlexa │ ├── PkgInfo │ ├── Resources │ ├── AlexaViewController.nib │ ├── AppIcon.icns │ ├── Assets.car │ ├── Base.lproj │ │ └── MainMenu.nib │ ├── DeviceHeroImg.png │ ├── StatusBarButtonImage@2x.png │ ├── login.html │ ├── mechoRecSmall.tiff │ ├── mechoRecord.png │ └── mechoico512.png │ └── _CodeSignature │ └── CodeResources ├── Cartfile ├── HackathonAlexa.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── kunal.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── kunal.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── HackathonAlexa.xcscheme │ └── xcschememanagement.plist ├── HackathonAlexa ├── AVSUploader.swift ├── AlexaStatusController.swift ├── AlexaViewController.swift ├── AlexaViewController.xib ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── DeviceHeroImg.png │ ├── RecIcon.imageset │ │ ├── Contents.json │ │ ├── mechoRecSmall.png │ │ └── mechoRecord.png │ ├── StatusBarButtonImage.imageset │ │ ├── Contents.json │ │ └── StatusBarButtonImage@2x.png │ ├── StatusBarButtonImage@2x.png │ ├── StatusBarButtonImage@2x.psd │ ├── mecho@2x.png │ ├── mechoRecSmall.png │ ├── mechoRecSmall@2x.png │ └── mechoRecord.png ├── Base.lproj │ └── MainMenu.xib ├── EventMonitor.swift ├── Info.plist ├── SimplePCMRecorder.swift ├── SimpleWebServer.swift ├── config.swift └── login.html ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/GCDWebServers: -------------------------------------------------------------------------------- 1 | Versions/Current/GCDWebServers -------------------------------------------------------------------------------- /Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/GCDWebServers: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/GCDWebServers -------------------------------------------------------------------------------- /Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/Info.plist -------------------------------------------------------------------------------- /Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.1.1 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | .btn-default, 8 | .btn-primary, 9 | .btn-success, 10 | .btn-info, 11 | .btn-warning, 12 | .btn-danger { 13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 16 | } 17 | .btn-default:active, 18 | .btn-primary:active, 19 | .btn-success:active, 20 | .btn-info:active, 21 | .btn-warning:active, 22 | .btn-danger:active, 23 | .btn-default.active, 24 | .btn-primary.active, 25 | .btn-success.active, 26 | .btn-info.active, 27 | .btn-warning.active, 28 | .btn-danger.active { 29 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 31 | } 32 | .btn:active, 33 | .btn.active { 34 | background-image: none; 35 | } 36 | .btn-default { 37 | text-shadow: 0 1px 0 #fff; 38 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 39 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 40 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 41 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 42 | background-repeat: repeat-x; 43 | border-color: #dbdbdb; 44 | border-color: #ccc; 45 | } 46 | .btn-default:hover, 47 | .btn-default:focus { 48 | background-color: #e0e0e0; 49 | background-position: 0 -15px; 50 | } 51 | .btn-default:active, 52 | .btn-default.active { 53 | background-color: #e0e0e0; 54 | border-color: #dbdbdb; 55 | } 56 | .btn-primary { 57 | background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%); 58 | background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%); 59 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); 60 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 61 | background-repeat: repeat-x; 62 | border-color: #2b669a; 63 | } 64 | .btn-primary:hover, 65 | .btn-primary:focus { 66 | background-color: #2d6ca2; 67 | background-position: 0 -15px; 68 | } 69 | .btn-primary:active, 70 | .btn-primary.active { 71 | background-color: #2d6ca2; 72 | border-color: #2b669a; 73 | } 74 | .btn-success { 75 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 76 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 77 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 78 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 79 | background-repeat: repeat-x; 80 | border-color: #3e8f3e; 81 | } 82 | .btn-success:hover, 83 | .btn-success:focus { 84 | background-color: #419641; 85 | background-position: 0 -15px; 86 | } 87 | .btn-success:active, 88 | .btn-success.active { 89 | background-color: #419641; 90 | border-color: #3e8f3e; 91 | } 92 | .btn-info { 93 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 94 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 95 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 96 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 97 | background-repeat: repeat-x; 98 | border-color: #28a4c9; 99 | } 100 | .btn-info:hover, 101 | .btn-info:focus { 102 | background-color: #2aabd2; 103 | background-position: 0 -15px; 104 | } 105 | .btn-info:active, 106 | .btn-info.active { 107 | background-color: #2aabd2; 108 | border-color: #28a4c9; 109 | } 110 | .btn-warning { 111 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 112 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 113 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 114 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 115 | background-repeat: repeat-x; 116 | border-color: #e38d13; 117 | } 118 | .btn-warning:hover, 119 | .btn-warning:focus { 120 | background-color: #eb9316; 121 | background-position: 0 -15px; 122 | } 123 | .btn-warning:active, 124 | .btn-warning.active { 125 | background-color: #eb9316; 126 | border-color: #e38d13; 127 | } 128 | .btn-danger { 129 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 130 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 131 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 132 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 133 | background-repeat: repeat-x; 134 | border-color: #b92c28; 135 | } 136 | .btn-danger:hover, 137 | .btn-danger:focus { 138 | background-color: #c12e2a; 139 | background-position: 0 -15px; 140 | } 141 | .btn-danger:active, 142 | .btn-danger.active { 143 | background-color: #c12e2a; 144 | border-color: #b92c28; 145 | } 146 | .thumbnail, 147 | .img-thumbnail { 148 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 149 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 150 | } 151 | .dropdown-menu > li > a:hover, 152 | .dropdown-menu > li > a:focus { 153 | background-color: #e8e8e8; 154 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 155 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 156 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 157 | background-repeat: repeat-x; 158 | } 159 | .dropdown-menu > .active > a, 160 | .dropdown-menu > .active > a:hover, 161 | .dropdown-menu > .active > a:focus { 162 | background-color: #357ebd; 163 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); 164 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 165 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 166 | background-repeat: repeat-x; 167 | } 168 | .navbar-default { 169 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 170 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 171 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 172 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 173 | background-repeat: repeat-x; 174 | border-radius: 4px; 175 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 176 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 177 | } 178 | .navbar-default .navbar-nav > .active > a { 179 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); 180 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%); 181 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0); 182 | background-repeat: repeat-x; 183 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 184 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 185 | } 186 | .navbar-brand, 187 | .navbar-nav > li > a { 188 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 189 | } 190 | .navbar-inverse { 191 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 192 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 193 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 194 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 195 | background-repeat: repeat-x; 196 | } 197 | .navbar-inverse .navbar-nav > .active > a { 198 | background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%); 199 | background-image: linear-gradient(to bottom, #222 0%, #282828 100%); 200 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0); 201 | background-repeat: repeat-x; 202 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 203 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 204 | } 205 | .navbar-inverse .navbar-brand, 206 | .navbar-inverse .navbar-nav > li > a { 207 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 208 | } 209 | .navbar-static-top, 210 | .navbar-fixed-top, 211 | .navbar-fixed-bottom { 212 | border-radius: 0; 213 | } 214 | .alert { 215 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 216 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 217 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 218 | } 219 | .alert-success { 220 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 221 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 222 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 223 | background-repeat: repeat-x; 224 | border-color: #b2dba1; 225 | } 226 | .alert-info { 227 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 228 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 229 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 230 | background-repeat: repeat-x; 231 | border-color: #9acfea; 232 | } 233 | .alert-warning { 234 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 235 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 236 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 237 | background-repeat: repeat-x; 238 | border-color: #f5e79e; 239 | } 240 | .alert-danger { 241 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 242 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 243 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 244 | background-repeat: repeat-x; 245 | border-color: #dca7a7; 246 | } 247 | .progress { 248 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 249 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 250 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 251 | background-repeat: repeat-x; 252 | } 253 | .progress-bar { 254 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%); 255 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); 256 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); 257 | background-repeat: repeat-x; 258 | } 259 | .progress-bar-success { 260 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 261 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 262 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 263 | background-repeat: repeat-x; 264 | } 265 | .progress-bar-info { 266 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 267 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 268 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 269 | background-repeat: repeat-x; 270 | } 271 | .progress-bar-warning { 272 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 273 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 274 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 275 | background-repeat: repeat-x; 276 | } 277 | .progress-bar-danger { 278 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 279 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 280 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 281 | background-repeat: repeat-x; 282 | } 283 | .list-group { 284 | border-radius: 4px; 285 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 286 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 287 | } 288 | .list-group-item.active, 289 | .list-group-item.active:hover, 290 | .list-group-item.active:focus { 291 | text-shadow: 0 -1px 0 #3071a9; 292 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%); 293 | background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); 294 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); 295 | background-repeat: repeat-x; 296 | border-color: #3278b3; 297 | } 298 | .panel { 299 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 300 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 301 | } 302 | .panel-default > .panel-heading { 303 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 304 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 305 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 306 | background-repeat: repeat-x; 307 | } 308 | .panel-primary > .panel-heading { 309 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); 310 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 311 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 312 | background-repeat: repeat-x; 313 | } 314 | .panel-success > .panel-heading { 315 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 316 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 317 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 318 | background-repeat: repeat-x; 319 | } 320 | .panel-info > .panel-heading { 321 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 322 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 323 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 324 | background-repeat: repeat-x; 325 | } 326 | .panel-warning > .panel-heading { 327 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 328 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 329 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 330 | background-repeat: repeat-x; 331 | } 332 | .panel-danger > .panel-heading { 333 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 334 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 335 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 336 | background-repeat: repeat-x; 337 | } 338 | .well { 339 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 340 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 341 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 342 | background-repeat: repeat-x; 343 | border-color: #dcdcdc; 344 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 345 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 346 | } 347 | /*# sourceMappingURL=bootstrap-theme.css.map */ 348 | -------------------------------------------------------------------------------- /Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/css/index.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2015, Pierre-Olivier Latour 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * The name of Pierre-Olivier Latour may not be used to endorse 13 | or promote products derived from this software without specific 14 | prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | .row-file { 29 | height: 40px; 30 | } 31 | 32 | .column-icon { 33 | width: 40px; 34 | text-align: center; 35 | } 36 | 37 | .column-name { 38 | } 39 | 40 | .column-size { 41 | width: 100px; 42 | text-align: right; 43 | } 44 | 45 | .column-move { 46 | width: 40px; 47 | text-align: center; 48 | } 49 | 50 | .column-delete { 51 | width: 40px; 52 | text-align: center; 53 | } 54 | 55 | .column-path { 56 | } 57 | 58 | .column-progress { 59 | width: 200px; 60 | } 61 | 62 | .footer { 63 | color: #999; 64 | text-align: center; 65 | font-size: 0.9em; 66 | } 67 | 68 | #reload { 69 | float: right; 70 | } 71 | 72 | #create-input { 73 | width: 50%; 74 | height: 20px; 75 | } 76 | 77 | #move-input { 78 | width: 80%; 79 | height: 20px; 80 | } 81 | 82 | /* Bootstrap overrides */ 83 | 84 | .btn:focus { 85 | outline: none; /* FIXME: Work around for Chrome only but still draws focus ring while button pressed */ 86 | } 87 | 88 | .btn-toolbar { 89 | margin-top: 30px; 90 | margin-bottom: 20px; 91 | } 92 | 93 | .table .progress { 94 | margin-top: 0px; 95 | margin-bottom: 0px; 96 | height: 16px; 97 | } 98 | 99 | .panel-default > .panel-heading { 100 | color: #555; 101 | } 102 | 103 | .breadcrumb { 104 | background-color: transparent; 105 | border-radius: 0px; 106 | margin-bottom: 0px; 107 | padding: 0px; 108 | } 109 | 110 | .breadcrumb > .active { 111 | color: #555; 112 | } 113 | 114 | .breadcrumb > li + li:before { 115 | color: #999; 116 | } 117 | 118 | .table > tbody > tr > td { 119 | vertical-align: middle; 120 | } 121 | 122 | .table > tbody > tr > td > p { 123 | margin: 0px; 124 | } 125 | 126 | /* Initial state */ 127 | 128 | .uploading { 129 | display: none; 130 | } 131 | -------------------------------------------------------------------------------- /Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/css/jquery.fileupload.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | /* 3 | * jQuery File Upload Plugin CSS 1.3.0 4 | * https://github.com/blueimp/jQuery-File-Upload 5 | * 6 | * Copyright 2013, Sebastian Tschan 7 | * https://blueimp.net 8 | * 9 | * Licensed under the MIT license: 10 | * http://www.opensource.org/licenses/MIT 11 | */ 12 | 13 | .fileinput-button { 14 | position: relative; 15 | overflow: hidden; 16 | } 17 | .fileinput-button input { 18 | position: absolute; 19 | top: 0; 20 | right: 0; 21 | margin: 0; 22 | opacity: 0; 23 | -ms-filter: 'alpha(opacity=0)'; 24 | font-size: 200px; 25 | direction: ltr; 26 | cursor: pointer; 27 | } 28 | 29 | /* Fixes for IE < 8 */ 30 | @media screen\9 { 31 | .fileinput-button input { 32 | filter: alpha(opacity=0); 33 | font-size: 100%; 34 | height: 100%; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | "PROLOGUE" = "

Drag & drop files on this window or use the \"Upload Files…\" button to upload new files.

"; 2 | "EPILOGUE" = ""; 3 | "FOOTER_FORMAT" = "%@ %@"; 4 | -------------------------------------------------------------------------------- /Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/index.html: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | %title% 35 | 36 | 37 | 38 | 39 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 53 | 54 | 55 | 56 | 57 |
58 | 59 | 62 | 63 | %prologue% 64 | 65 |
66 | 67 |
68 | 72 | 75 | 78 |
79 | 80 |
81 |
File Uploads in Progress
82 |
83 |
84 | 85 |
86 |
87 | 88 |
89 |
90 |
91 | 92 | %epilogue% 93 | 94 | 95 | 96 |
97 | 98 | 118 | 119 | 139 | 140 | 171 | 172 | 187 | 188 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /Binary/HackathonAlexa.app/Contents/Frameworks/GCDWebServers.framework/Versions/A/Resources/GCDWebUploader.bundle/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f); 8 | if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d= 1000000000) { 36 | return (bytes / 1000000000).toFixed(2) + ' GB'; 37 | } 38 | if (bytes >= 1000000) { 39 | return (bytes / 1000000).toFixed(2) + ' MB'; 40 | } 41 | return (bytes / 1000).toFixed(2) + ' KB'; 42 | } 43 | 44 | function _showError(message, textStatus, errorThrown) { 45 | $("#alerts").prepend(tmpl("template-alert", { 46 | level: "danger", 47 | title: (errorThrown != "" ? errorThrown : textStatus) + ": ", 48 | description: message 49 | })); 50 | } 51 | 52 | function _disableReloads() { 53 | _reloadingDisabled += 1; 54 | } 55 | 56 | function _enableReloads() { 57 | _reloadingDisabled -= 1; 58 | 59 | if (_pendingReloads.length > 0) { 60 | _reload(_pendingReloads.shift()); 61 | } 62 | } 63 | 64 | function _reload(path) { 65 | if (_reloadingDisabled) { 66 | if ($.inArray(path, _pendingReloads) < 0) { 67 | _pendingReloads.push(path); 68 | } 69 | return; 70 | } 71 | 72 | _disableReloads(); 73 | $.ajax({ 74 | url: 'list', 75 | type: 'GET', 76 | data: {path: path}, 77 | dataType: 'json' 78 | }).fail(function(jqXHR, textStatus, errorThrown) { 79 | _showError("Failed retrieving contents of \"" + path + "\"", textStatus, errorThrown); 80 | }).done(function(data, textStatus, jqXHR) { 81 | var scrollPosition = $(document).scrollTop(); 82 | 83 | if (path != _path) { 84 | $("#path").empty(); 85 | if (path == "/") { 86 | $("#path").append('
  • ' + _device + '
  • '); 87 | } else { 88 | $("#path").append('
  • ' + _device + '
  • '); 89 | var components = path.split("/").slice(1, -1); 90 | for (var i = 0; i < components.length - 1; ++i) { 91 | var subpath = "/" + components.slice(0, i + 1).join("/") + "/"; 92 | $("#path").append('
  • ' + components[i] + '
  • '); 93 | } 94 | $("#path > li").click(function(event) { 95 | _reload($(this).data("path")); 96 | event.preventDefault(); 97 | }); 98 | $("#path").append('
  • ' + components[components.length - 1] + '
  • '); 99 | } 100 | _path = path; 101 | } 102 | 103 | $("#listing").empty(); 104 | for (var i = 0, file; file = data[i]; ++i) { 105 | $(tmpl("template-listing", file)).data(file).appendTo("#listing"); 106 | } 107 | 108 | $(".edit").editable(function(value, settings) { 109 | var name = $(this).parent().parent().data("name"); 110 | if (value != name) { 111 | var path = $(this).parent().parent().data("path"); 112 | $.ajax({ 113 | url: 'move', 114 | type: 'POST', 115 | data: {oldPath: path, newPath: _path + value}, 116 | dataType: 'json' 117 | }).fail(function(jqXHR, textStatus, errorThrown) { 118 | _showError("Failed moving \"" + path + "\" to \"" + _path + value + "\"", textStatus, errorThrown); 119 | }).always(function() { 120 | _reload(_path); 121 | }); 122 | } 123 | return value; 124 | }, { 125 | onedit: function(settings, original) { 126 | _disableReloads(); 127 | }, 128 | onsubmit: function(settings, original) { 129 | _enableReloads(); 130 | }, 131 | onreset: function(settings, original) { 132 | _enableReloads(); 133 | }, 134 | tooltip: 'Click to rename...' 135 | }); 136 | 137 | $(".button-download").click(function(event) { 138 | var path = $(this).parent().parent().data("path"); 139 | setTimeout(function() { 140 | window.location = "download?path=" + encodeURIComponent(path); 141 | }, 0); 142 | }); 143 | 144 | $(".button-open").click(function(event) { 145 | var path = $(this).parent().parent().data("path"); 146 | _reload(path); 147 | }); 148 | 149 | $(".button-move").click(function(event) { 150 | var path = $(this).parent().parent().data("path"); 151 | if (path[path.length - 1] == "/") { 152 | path = path.slice(0, path.length - 1); 153 | } 154 | $("#move-input").data("path", path); 155 | $("#move-input").val(path); 156 | $("#move-modal").modal("show"); 157 | }); 158 | 159 | $(".button-delete").click(function(event) { 160 | var path = $(this).parent().parent().data("path"); 161 | $.ajax({ 162 | url: 'delete', 163 | type: 'POST', 164 | data: {path: path}, 165 | dataType: 'json' 166 | }).fail(function(jqXHR, textStatus, errorThrown) { 167 | _showError("Failed deleting \"" + path + "\"", textStatus, errorThrown); 168 | }).always(function() { 169 | _reload(_path); 170 | }); 171 | }); 172 | 173 | $(document).scrollTop(scrollPosition); 174 | }).always(function() { 175 | _enableReloads(); 176 | }); 177 | } 178 | 179 | $(document).ready(function() { 180 | 181 | // Workaround Firefox and IE not showing file selection dialog when clicking on "upload-file" 78 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /HackathonAlexa/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // HackathonAlexa 4 | // 5 | // Created by KUNAL BATRA on 5/16/16. 6 | // Copyright © 2016 Kunal Batra. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | @IBOutlet weak var window: NSWindow! 15 | let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-2) 16 | let popover = NSPopover() 17 | var eventMonitor: EventMonitor? 18 | var recordbool = false 19 | 20 | 21 | func applicationDidFinishLaunching(aNotification: NSNotification) { 22 | if let button = statusItem.button { 23 | button.image = NSImage(named: "StatusBarButtonImage") 24 | button.action = #selector(self.togglePopover(_:)) 25 | } 26 | 27 | 28 | popover.contentViewController = AlexaViewController() 29 | let alexa = AlexaStatusController() 30 | alexa.awakeFromNib() 31 | 32 | eventMonitor = EventMonitor(mask: .FlagsChangedMask ) { [unowned self] event in 33 | if (event?.keyCode == 58 && !self.recordbool){ 34 | 35 | print("recording") 36 | self.recordbool = true 37 | alexa.toggleRecord() 38 | } 39 | else if (event?.keyCode == 58 && self.recordbool){ 40 | 41 | print ("stopped recording") 42 | self.recordbool = false 43 | alexa.toggleRecord() 44 | } 45 | 46 | 47 | } 48 | eventMonitor?.start() 49 | 50 | } 51 | 52 | func applicationWillTerminate(aNotification: NSNotification) { 53 | // Insert code here to tear down your application 54 | } 55 | 56 | func showPopover(sender: AnyObject?) { 57 | if let button = statusItem.button { 58 | popover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: NSRectEdge.MinY) 59 | } 60 | } 61 | 62 | func closePopover(sender: AnyObject?) { 63 | popover.performClose(sender) 64 | } 65 | 66 | func togglePopover(sender: AnyObject?) { 67 | if popover.shown { 68 | closePopover(sender) 69 | } else { 70 | showPopover(sender) 71 | } 72 | } 73 | 74 | 75 | 76 | } 77 | 78 | -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/DeviceHeroImg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/HackathonAlexa/Assets.xcassets/DeviceHeroImg.png -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/RecIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "filename" : "mechoRecSmall.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "filename" : "mechoRecord.png", 11 | "scale" : "2x" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/RecIcon.imageset/mechoRecSmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/HackathonAlexa/Assets.xcassets/RecIcon.imageset/mechoRecSmall.png -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/RecIcon.imageset/mechoRecord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/HackathonAlexa/Assets.xcassets/RecIcon.imageset/mechoRecord.png -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/StatusBarButtonImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "StatusBarButtonImage@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | }, 16 | { 17 | "idiom" : "mac", 18 | "scale" : "1x" 19 | }, 20 | { 21 | "idiom" : "mac", 22 | "scale" : "2x" 23 | } 24 | ], 25 | "info" : { 26 | "version" : 1, 27 | "author" : "xcode" 28 | } 29 | } -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/StatusBarButtonImage.imageset/StatusBarButtonImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/HackathonAlexa/Assets.xcassets/StatusBarButtonImage.imageset/StatusBarButtonImage@2x.png -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/StatusBarButtonImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/HackathonAlexa/Assets.xcassets/StatusBarButtonImage@2x.png -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/StatusBarButtonImage@2x.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/HackathonAlexa/Assets.xcassets/StatusBarButtonImage@2x.psd -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/mecho@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/HackathonAlexa/Assets.xcassets/mecho@2x.png -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/mechoRecSmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/HackathonAlexa/Assets.xcassets/mechoRecSmall.png -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/mechoRecSmall@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/HackathonAlexa/Assets.xcassets/mechoRecSmall@2x.png -------------------------------------------------------------------------------- /HackathonAlexa/Assets.xcassets/mechoRecord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kunal732/MacLexa/b5394b63225f98ec20c0afb5bd856a4dd8f93cc1/HackathonAlexa/Assets.xcassets/mechoRecord.png -------------------------------------------------------------------------------- /HackathonAlexa/EventMonitor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventMonitor.swift 3 | // HackathonAlexa 4 | // 5 | // Created by KUNAL BATRA on 5/16/16. 6 | // Copyright © 2016 Kunal Batra. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | public class EventMonitor { 12 | private var monitor: AnyObject? 13 | private let mask: NSEventMask 14 | private let handler: NSEvent? -> () 15 | 16 | public init(mask: NSEventMask, handler: NSEvent? -> ()) { 17 | self.mask = mask 18 | self.handler = handler 19 | } 20 | 21 | deinit { 22 | stop() 23 | } 24 | 25 | public func start() { 26 | monitor = NSEvent.addGlobalMonitorForEventsMatchingMask(mask, handler: handler) 27 | } 28 | 29 | public func stop() { 30 | if monitor != nil { 31 | NSEvent.removeMonitor(monitor!) 32 | monitor = nil 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /HackathonAlexa/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2016 Kunal Batra. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | LSUIElement 32 | 33 | CFBundleURLTypes 34 | 35 | 36 | CFBundleURLName 37 | HackathonAlexa 38 | CFBundleURLSchemes 39 | 40 | hackathonalexa 41 | 42 | 43 | 44 | NSPrincipalClass 45 | NSApplication 46 | 47 | 48 | -------------------------------------------------------------------------------- /HackathonAlexa/SimplePCMRecorder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SimplePCMRecorder.swift 3 | 4 | import Foundation 5 | 6 | import Foundation 7 | import CoreAudio 8 | import AudioToolbox 9 | 10 | struct RecorderState { 11 | var setupComplete: Bool 12 | var dataFormat: AudioStreamBasicDescription 13 | var queue: UnsafeMutablePointer 14 | var buffers: [AudioQueueBufferRef] 15 | var recordFile: AudioFileID 16 | var bufferByteSize: UInt32 17 | var currentPacket: Int64 18 | var isRunning: Bool 19 | var recordPacket: Int64 20 | var errorHandler: ((error:NSError) -> Void)? 21 | } 22 | 23 | class SimplePCMRecorder { 24 | 25 | private var recorderState: RecorderState 26 | 27 | init(numberBuffers:Int) { 28 | self.recorderState = RecorderState( 29 | setupComplete: false, 30 | dataFormat: AudioStreamBasicDescription(), 31 | queue: UnsafeMutablePointer.alloc(1), 32 | buffers: Array(count: numberBuffers, repeatedValue: nil), 33 | recordFile:AudioFileID(), 34 | bufferByteSize: 0, 35 | currentPacket: 0, 36 | isRunning: false, 37 | recordPacket: 0, 38 | errorHandler: nil) 39 | } 40 | 41 | deinit { 42 | self.recorderState.queue.dealloc(1) 43 | } 44 | 45 | func setupForRecording(outputFileName:String, sampleRate:Float64, channels:UInt32, bitsPerChannel:UInt32, errorHandler: ((error:NSError) -> Void)?) throws { 46 | self.recorderState.dataFormat.mFormatID = kAudioFormatLinearPCM 47 | self.recorderState.dataFormat.mSampleRate = sampleRate 48 | self.recorderState.dataFormat.mChannelsPerFrame = channels 49 | self.recorderState.dataFormat.mBitsPerChannel = bitsPerChannel 50 | self.recorderState.dataFormat.mFramesPerPacket = 1 51 | self.recorderState.dataFormat.mBytesPerFrame = self.recorderState.dataFormat.mChannelsPerFrame * (self.recorderState.dataFormat.mBitsPerChannel / 8) 52 | self.recorderState.dataFormat.mBytesPerPacket = self.recorderState.dataFormat.mBytesPerFrame * self.recorderState.dataFormat.mFramesPerPacket 53 | 54 | self.recorderState.dataFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked 55 | 56 | self.recorderState.errorHandler = errorHandler 57 | 58 | try osReturningCall { AudioFileCreateWithURL(NSURL(fileURLWithPath: outputFileName), kAudioFileWAVEType, &self.recorderState.dataFormat, AudioFileFlags.DontPageAlignAudioData.union(.EraseFile), &self.recorderState.recordFile) } 59 | 60 | self.recorderState.setupComplete = true 61 | } 62 | 63 | func startRecording() throws { 64 | 65 | guard self.recorderState.setupComplete else { throw NSError(domain: Config.Error.ErrorDomain, code: Config.Error.PCMSetupIncompleteErrorCode, userInfo: [NSLocalizedDescriptionKey : "Setup needs to be called before starting"]) } 66 | 67 | let osAQNI = AudioQueueNewInput(&self.recorderState.dataFormat, { (inUserData:UnsafeMutablePointer, inAQ:AudioQueueRef, inBuffer:AudioQueueBufferRef, inStartTime:UnsafePointer, inNumPackets:UInt32, inPacketDesc:UnsafePointer) -> Void in 68 | 69 | let internalRSP = unsafeBitCast(inUserData, UnsafeMutablePointer.self) 70 | 71 | if inNumPackets > 0 { 72 | var packets = inNumPackets 73 | 74 | let os = AudioFileWritePackets(internalRSP.memory.recordFile, false, inBuffer.memory.mAudioDataByteSize, inPacketDesc, internalRSP.memory.recordPacket, &packets, inBuffer.memory.mAudioData) 75 | if os != 0 && internalRSP.memory.errorHandler != nil { 76 | internalRSP.memory.errorHandler!(error:NSError(domain: NSOSStatusErrorDomain, code: Int(os), userInfo: nil)) 77 | } 78 | 79 | internalRSP.memory.recordPacket += Int64(packets) 80 | } 81 | 82 | if internalRSP.memory.isRunning { 83 | let os = AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil) 84 | if os != 0 && internalRSP.memory.errorHandler != nil { 85 | internalRSP.memory.errorHandler!(error:NSError(domain: NSOSStatusErrorDomain, code: Int(os), userInfo: nil)) 86 | } 87 | } 88 | 89 | }, &self.recorderState, nil, nil, 0, self.recorderState.queue) 90 | 91 | guard osAQNI == 0 else { throw NSError(domain: NSOSStatusErrorDomain, code: Int(osAQNI), userInfo: nil) } 92 | 93 | let bufferByteSize = try self.computeRecordBufferSize(self.recorderState.dataFormat, seconds: 0.5) 94 | for (var i = 0; i < self.recorderState.buffers.count; ++i) { 95 | try osReturningCall { AudioQueueAllocateBuffer(self.recorderState.queue.memory, UInt32(bufferByteSize), &self.recorderState.buffers[i]) } 96 | 97 | try osReturningCall { AudioQueueEnqueueBuffer(self.recorderState.queue.memory, self.recorderState.buffers[i], 0, nil) } 98 | } 99 | 100 | try osReturningCall { AudioQueueStart(self.recorderState.queue.memory, nil) } 101 | 102 | self.recorderState.isRunning = true 103 | } 104 | 105 | func stopRecording() throws { 106 | self.recorderState.isRunning = false 107 | 108 | try osReturningCall { AudioQueueStop(self.recorderState.queue.memory, true) } 109 | try osReturningCall { AudioQueueDispose(self.recorderState.queue.memory, true) } 110 | try osReturningCall { AudioFileClose(self.recorderState.recordFile) } 111 | } 112 | 113 | private func computeRecordBufferSize(format:AudioStreamBasicDescription, seconds:Double) throws -> Int { 114 | 115 | let framesNeededForBufferTime = Int(ceil(seconds * format.mSampleRate)) 116 | 117 | if format.mBytesPerFrame > 0 { 118 | return framesNeededForBufferTime * Int(format.mBytesPerFrame) 119 | } else { 120 | var maxPacketSize = UInt32(0) 121 | 122 | if format.mBytesPerPacket > 0 { 123 | maxPacketSize = format.mBytesPerPacket 124 | } else { 125 | try self.getAudioQueueProperty(kAudioQueueProperty_MaximumOutputPacketSize, value: &maxPacketSize) 126 | } 127 | 128 | var packets = 0 129 | if format.mFramesPerPacket > 0 { 130 | packets = framesNeededForBufferTime / Int(format.mFramesPerPacket) 131 | } else { 132 | packets = framesNeededForBufferTime 133 | } 134 | 135 | if packets == 0 { 136 | packets = 1 137 | } 138 | 139 | return packets * Int(maxPacketSize) 140 | } 141 | 142 | } 143 | 144 | private func osReturningCall(osCall: () -> OSStatus) throws { 145 | let os = osCall() 146 | if os != 0 { 147 | throw NSError(domain: NSOSStatusErrorDomain, code: Int(os), userInfo: nil) 148 | } 149 | } 150 | 151 | private func getAudioQueueProperty(propertyId:AudioQueuePropertyID, inout value:T) throws { 152 | 153 | let propertySize = UnsafeMutablePointer.alloc(1) 154 | propertySize.memory = UInt32(sizeof(T)) 155 | 156 | let os = AudioQueueGetProperty(self.recorderState.queue.memory, 157 | propertyId, 158 | &value, 159 | propertySize) 160 | 161 | propertySize.dealloc(1) 162 | 163 | guard os == 0 else { throw NSError(domain: NSOSStatusErrorDomain, code: Int(os), userInfo: nil) } 164 | 165 | } 166 | } -------------------------------------------------------------------------------- /HackathonAlexa/SimpleWebServer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleWebServer.swift 3 | // 4 | 5 | import Foundation 6 | import GCDWebServers 7 | 8 | protocol SimpleWebServerDelegate { 9 | func startupComplete(webServerURL: NSURL) 10 | func configurationComplete(tokenExpirationTime: NSDate, currentAccessToken: String) 11 | } 12 | 13 | class SimpleWebServer: NSObject, GCDWebServerDelegate { 14 | 15 | static let instance = SimpleWebServer() 16 | 17 | var delegate: SimpleWebServerDelegate? 18 | 19 | private let webServer = GCDWebServer() 20 | private var loginHTML: String? 21 | 22 | func startWebServer() { 23 | let defaults = NSUserDefaults.standardUserDefaults() 24 | let appidString = defaults.objectForKey("AppID") as! String 25 | let clientidString = defaults.objectForKey("ClientID") as! String 26 | let devserialString = clientidString 27 | print("AppID: \(appidString)") 28 | print("ClientID: \(clientidString)") 29 | print("SerialID: \(devserialString)") 30 | 31 | self.webServer.delegate = self 32 | 33 | if loginHTML == nil { 34 | if let rootPath = NSBundle.mainBundle().resourcePath, let loginData = NSData(contentsOfFile: "\(rootPath)/login.html") { 35 | var html = NSString(data: loginData, encoding: NSUTF8StringEncoding) 36 | /* 37 | html = html?.stringByReplacingOccurrencesOfString("#{client_id}", withString: Config.LoginWithAmazon.ClientId) 38 | html = html?.stringByReplacingOccurrencesOfString("#{device_serial_number}", withString: Config.LoginWithAmazon.DeviceSerialNumber) 39 | html = html?.stringByReplacingOccurrencesOfString("#{product_id}", withString: Config.LoginWithAmazon.ProductId)*/ 40 | 41 | html = html?.stringByReplacingOccurrencesOfString("#{client_id}", withString: clientidString) 42 | html = html?.stringByReplacingOccurrencesOfString("#{device_serial_number}", withString: devserialString) 43 | html = html?.stringByReplacingOccurrencesOfString("#{product_id}", withString: appidString) 44 | 45 | loginHTML = html as String? 46 | 47 | } else { 48 | loginHTML = "Error loading login html." 49 | } 50 | } 51 | 52 | webServer.addDefaultHandlerForMethod("GET", requestClass: GCDWebServerRequest.self, processBlock: {request in 53 | return GCDWebServerDataResponse(HTML:self.loginHTML) 54 | }) 55 | 56 | webServer.addHandlerForMethod("GET", path: "/complete", requestClass: GCDWebServerRequest.self) { (request:GCDWebServerRequest!) -> GCDWebServerResponse! in 57 | 58 | if let expiresIn = request.query["expires_in"] as? String, let accessToken = request.query["access_token"] as? String { 59 | print("token: \(accessToken)") 60 | var tokenExpirationTime: NSDate? 61 | var currentAccessToken: String? 62 | 63 | if let eid = Double(expiresIn) { 64 | tokenExpirationTime = NSDate().dateByAddingTimeInterval(eid) 65 | } else { 66 | tokenExpirationTime = NSDate() 67 | } 68 | currentAccessToken = accessToken 69 | let defaults = NSUserDefaults.standardUserDefaults() 70 | defaults.setObject(accessToken, forKey: "token") 71 | defaults.setObject(tokenExpirationTime, forKey: "expiresIn") 72 | 73 | if tokenExpirationTime != nil && currentAccessToken != nil { 74 | self.delegate?.configurationComplete(tokenExpirationTime!, currentAccessToken: currentAccessToken!) 75 | } 76 | 77 | return GCDWebServerDataResponse(HTML: "Login complete, you can go back to the app now.") 78 | 79 | } else { 80 | return GCDWebServerDataResponse(HTML: "Error logging in, Try again") 81 | } 82 | 83 | } 84 | 85 | webServer.startWithPort(8777, bonjourName: "GCD Web Server") 86 | 87 | } 88 | 89 | // 90 | // GCDWebServerDelegate Impl 91 | // 92 | 93 | func webServerDidStart(server: GCDWebServer!) { 94 | delegate?.startupComplete(server.serverURL) 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /HackathonAlexa/config.swift: -------------------------------------------------------------------------------- 1 | // 2 | // config.swift 3 | 4 | import Foundation 5 | 6 | struct Config { 7 | 8 | struct Debug { 9 | static let General = true 10 | static let Errors = true 11 | static let HTTPRequest = true 12 | static let HTTPResponse = true 13 | } 14 | 15 | struct Error { 16 | static let ErrorDomain = "net.ioncannon.SimplePCMRecorderError" 17 | 18 | static let PCMSetupIncompleteErrorCode = 1 19 | 20 | static let AVSUploaderSetupIncompleteErrorCode = 2 21 | static let AVSAPICallErrorCode = 3 22 | static let AVSResponseBorderParseErrorCode = 4 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /HackathonAlexa/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
    4 | 5 | 18 | 19 |

    20 | 21 | Login: 22 | 23 | Login with Amazon 24 |
    25 |

    26 | 27 |

    28 | Logout: Logout
    29 |

    30 | 31 | 46 | 47 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kunal Batra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MacLexa 2 | ![Alt text](http://i.imgur.com/BzSViXy.png "MacLexa System Tray Expanded") 3 | ## About the Project 4 | Built an Alexa App which lives in the Mac System Tray. It can be accessed at any time by pressing and holding the Option key when speaking commands. 5 | 6 | Big Shoutout to [Carson McDonald](https://github.com/carsonmcdonald) whose [AVSExample](https://github.com/carsonmcdonald/AVSExample-Swift) in Swift laid the foundation for this project. 7 | ___ 8 | 9 | ##Benefits of this app vs the Amazon Echo 10 | 11 | 1. You can do client side processing on the Audio (i.e: determine who is speaking, get sentiment or emotion analysis...etc) 12 | 2. It's much easier for demoing the Amazon skill you just created at a Hackathon 13 | 3. It's Free! ;) 14 | 15 | --- 16 | ##Getting Started 17 | ###Just want the Binary? 18 | Skip all the instructions below. Clone the repo and run the app in the Binary Folder. I believe you still [need an Alexa account](alexa.amazon.com). 19 | 20 | ###Amazon Developer Account 21 | 1. Create an account at developer.amazon.com 22 | 2. Follow the [getting started guide](https://developer.amazon.com/appsandservices/solutions/alexa/alexa-voice-service/getting-started-with-the-alexa-voice-service) for Amazon Voice Service. 23 | 3. In the procedure keep track of your Application Type ID and Client ID. We will need those values later. 24 | 25 | ###Install Dependencies 26 | [Carthage](https://github.com/Carthage/Carthage) is used for dependancies. After cloning go into the root directory and run: 27 | 28 | ``` 29 | carthage bootstrap 30 | ``` 31 | Since this is Carthage, make sure to add the GCDframework for Mac OSX to the project after your done running the bootstrap command. 32 | 33 | ###Enter Config Values 34 | ![Alt text](http://i.imgur.com/gjoth5j.png "MacLexa System Tray") 35 | - Click on the MacLexa icon to show the Nib. 36 | - Enter in the Application Type ID and Client ID 37 | - Register 38 | 39 | 40 | ###Begin! 41 | ![Alt text](http://i.imgur.com/D3aVC6j.png "MacLexa Recording in the System Tray") 42 | - Press the Option Key to start recording, on first press it will see you dont have a token and open up a safari window for you to enter your amazon creds 43 | - Hold and Press the option key while you're talking to Alexa. You will see a red record icon pop up become visible while you are speaking to alexa. 44 | 45 | --- 46 | email kunal@debug.io for questions 47 | --------------------------------------------------------------------------------