├── Focusin Logo ├── 1024x1024.png ├── 128x128.png ├── 16x16.png ├── 24x24.png ├── 256x256.png ├── 32x32.png ├── 48x48.png ├── 512x512.png ├── 64x64.png └── 96x96.png ├── Focusin.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── alberto.xcuserdatad │ └── xcschemes │ ├── Pomodoro.xcscheme │ └── xcschememanagement.plist ├── Focusin ├── About │ ├── AboutWindowController.swift │ └── AboutWindowController.xib ├── AppDelegate.swift ├── Assets.xcassets │ ├── 128x128.imageset │ │ ├── 128x128.png │ │ └── Contents.json │ ├── 256x256.imageset │ │ ├── 256x256.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── 1024x1024.png │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 256x256-1.png │ │ ├── 256x256.png │ │ ├── 32x32-1.png │ │ ├── 32x32.png │ │ ├── 512x512-1.png │ │ ├── 512x512.png │ │ ├── 64x64.png │ │ └── Contents.json │ ├── Contents.json │ ├── Hacking.imageset │ │ ├── Contents.json │ │ └── Hacking.png │ ├── back.imageset │ │ ├── Contents.json │ │ └── back.png │ ├── cancel-1.imageset │ │ ├── Contents.json │ │ └── cancel.png │ ├── cancel.imageset │ │ ├── Contents.json │ │ └── cancel.png │ ├── checked.imageset │ │ ├── Contents.json │ │ └── checked.png │ ├── circle_thin.imageset │ │ ├── Contents.json │ │ └── circle_thin.png │ ├── circled_left_2.imageset │ │ ├── Contents.json │ │ └── circled_left_2.png │ ├── circled_left_2_filled.imageset │ │ ├── Contents.json │ │ └── circled_left_2_filled.png │ ├── delete.imageset │ │ ├── Contents.json │ │ └── delete.png │ ├── delete_sign-1.imageset │ │ ├── Contents.json │ │ └── delete_sign.png │ ├── delete_sign.imageset │ │ ├── Contents.json │ │ └── delete_sign.png │ ├── github.imageset │ │ ├── Contents.json │ │ └── github.png │ ├── github_2.imageset │ │ ├── Contents.json │ │ └── github_2.png │ ├── goal-1.imageset │ │ ├── Contents.json │ │ └── goal.png │ ├── goal.imageset │ │ ├── Contents.json │ │ └── goal.png │ ├── goal_filled.imageset │ │ ├── Contents.json │ │ └── goal_filled.png │ ├── icons8_logo.imageset │ │ ├── Contents.json │ │ └── icons8_logo.png │ ├── left_round.imageset │ │ ├── Contents.json │ │ └── left_round.png │ ├── minus.imageset │ │ ├── Contents.json │ │ └── minus.png │ ├── ok-1.imageset │ │ ├── Contents.json │ │ └── ok.png │ ├── ok.imageset │ │ ├── Contents.json │ │ └── ok.png │ ├── pause-1.imageset │ │ ├── Contents.json │ │ └── pause.png │ ├── pause-2.imageset │ │ ├── Contents.json │ │ └── pause.png │ ├── pause-3.imageset │ │ ├── Contents.json │ │ └── pause.png │ ├── pause.imageset │ │ ├── Contents.json │ │ └── pause.png │ ├── pause_filled.imageset │ │ ├── Contents.json │ │ └── pause_filled.png │ ├── play-1.imageset │ │ ├── Contents.json │ │ └── play.png │ ├── play-2.imageset │ │ ├── Contents.json │ │ └── play.png │ ├── play-3.imageset │ │ ├── Contents.json │ │ └── play.png │ ├── play.imageset │ │ ├── Contents.json │ │ └── play.png │ ├── play_round.imageset │ │ ├── Contents.json │ │ └── play_round.png │ ├── settings-1.imageset │ │ ├── Contents.json │ │ └── settings.png │ ├── settings-m.imageset │ │ ├── Contents.json │ │ └── horizontal_settings_mixer.png │ ├── settings-m1.imageset │ │ ├── Contents.json │ │ └── horizontal_settings_mixer_filled.png │ ├── settings.imageset │ │ ├── Contents.json │ │ └── settings.png │ ├── sofa.imageset │ │ ├── Contents.json │ │ └── sofa.png │ ├── sofa_filled.imageset │ │ ├── Contents.json │ │ └── sofa_filled.png │ ├── timer-1.imageset │ │ ├── Contents.json │ │ └── timer.png │ ├── timer-2.imageset │ │ ├── Contents.json │ │ └── timer.png │ ├── timer-3.imageset │ │ ├── Contents.json │ │ └── timer.png │ ├── timer.imageset │ │ ├── Contents.json │ │ └── timer.png │ ├── todo_list-1.imageset │ │ ├── Contents.json │ │ └── todo_list.png │ ├── todo_list-2.imageset │ │ ├── Contents.json │ │ └── todo_list.png │ ├── todo_list.imageset │ │ ├── Contents.json │ │ └── todo_list.png │ └── tomato.imageset │ │ ├── Contents.json │ │ └── tomato.png ├── Base.lproj │ └── MainMenu.xib ├── CircleAnimation.swift ├── Defaults.swift ├── EventMonitor.swift ├── Info.plist ├── NotificationsHandler.swift ├── PlistManager.swift ├── PomodoroViewController.swift ├── PomodoroViewController.xib ├── PopoverRootView.swift ├── Preferences │ ├── PreferencesWindowController.swift │ └── PreferencesWindowController.xib ├── TaskKeys.swift ├── Tasks │ ├── Tasks.plist │ ├── TasksViewController.swift │ └── TasksViewController.xib └── Timer.swift ├── LICENSE ├── LauncherApplication ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── Main.storyboard ├── Info.plist ├── LauncherApplication.entitlements └── ViewController.swift ├── Pomodoro.entitlements ├── README.md └── focusin.png /Focusin Logo/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin Logo/1024x1024.png -------------------------------------------------------------------------------- /Focusin Logo/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin Logo/128x128.png -------------------------------------------------------------------------------- /Focusin Logo/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin Logo/16x16.png -------------------------------------------------------------------------------- /Focusin Logo/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin Logo/24x24.png -------------------------------------------------------------------------------- /Focusin Logo/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin Logo/256x256.png -------------------------------------------------------------------------------- /Focusin Logo/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin Logo/32x32.png -------------------------------------------------------------------------------- /Focusin Logo/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin Logo/48x48.png -------------------------------------------------------------------------------- /Focusin Logo/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin Logo/512x512.png -------------------------------------------------------------------------------- /Focusin Logo/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin Logo/64x64.png -------------------------------------------------------------------------------- /Focusin Logo/96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin Logo/96x96.png -------------------------------------------------------------------------------- /Focusin.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D338D4351D107E9D00BB86C1 /* NotificationsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D338D4341D107E9D00BB86C1 /* NotificationsHandler.swift */; }; 11 | D338D4371D10842900BB86C1 /* CircleAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D338D4361D10842900BB86C1 /* CircleAnimation.swift */; }; 12 | D338D4391D108FEE00BB86C1 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D338D4381D108FEE00BB86C1 /* Defaults.swift */; }; 13 | D338D4431D1140B600BB86C1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D338D4421D1140B600BB86C1 /* AppDelegate.swift */; }; 14 | D338D4451D1140B600BB86C1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D338D4441D1140B600BB86C1 /* ViewController.swift */; }; 15 | D338D4471D1140B600BB86C1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D338D4461D1140B600BB86C1 /* Assets.xcassets */; }; 16 | D338D44A1D1140B600BB86C1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D338D4481D1140B600BB86C1 /* Main.storyboard */; }; 17 | D338D4511D1141E900BB86C1 /* LauncherApplication.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = D338D4401D1140B600BB86C1 /* LauncherApplication.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 18 | D338D4531D11421500BB86C1 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D338D4521D11421500BB86C1 /* ServiceManagement.framework */; }; 19 | D338D4551D11422000BB86C1 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D338D4541D11422000BB86C1 /* Cocoa.framework */; }; 20 | D338D45A1D11AD7B00BB86C1 /* TasksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D338D4581D11AD7B00BB86C1 /* TasksViewController.swift */; }; 21 | D338D45B1D11AD7B00BB86C1 /* TasksViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D338D4591D11AD7B00BB86C1 /* TasksViewController.xib */; }; 22 | D3E8E1991D13E6D10084B664 /* TaskKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3E8E1981D13E6D10084B664 /* TaskKeys.swift */; }; 23 | D3E8E19A1D13EB570084B664 /* Tasks.plist in Resources */ = {isa = PBXBuildFile; fileRef = D3E8E1961D13E5C80084B664 /* Tasks.plist */; }; 24 | D3E8E19C1D13F3550084B664 /* PlistManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3E8E19B1D13F3550084B664 /* PlistManager.swift */; }; 25 | D3ECECBF1D1033BF0069BC94 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3ECECBE1D1033BF0069BC94 /* AppDelegate.swift */; }; 26 | D3ECECC11D1033C00069BC94 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D3ECECC01D1033C00069BC94 /* Assets.xcassets */; }; 27 | D3ECECC41D1033C00069BC94 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D3ECECC21D1033C00069BC94 /* MainMenu.xib */; }; 28 | D3ECECD21D1034860069BC94 /* AboutWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3ECECD01D1034860069BC94 /* AboutWindowController.swift */; }; 29 | D3ECECD31D1034860069BC94 /* AboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D3ECECD11D1034860069BC94 /* AboutWindowController.xib */; }; 30 | D3ECECD71D1034AC0069BC94 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3ECECD51D1034AC0069BC94 /* PreferencesWindowController.swift */; }; 31 | D3ECECD81D1034AC0069BC94 /* PreferencesWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D3ECECD61D1034AC0069BC94 /* PreferencesWindowController.xib */; }; 32 | D3ECECDF1D1034F70069BC94 /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3ECECDA1D1034F70069BC94 /* EventMonitor.swift */; }; 33 | D3ECECE01D1034F70069BC94 /* PomodoroViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3ECECDB1D1034F70069BC94 /* PomodoroViewController.swift */; }; 34 | D3ECECE11D1034F70069BC94 /* PomodoroViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D3ECECDC1D1034F70069BC94 /* PomodoroViewController.xib */; }; 35 | D3ECECE21D1034F70069BC94 /* PopoverRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3ECECDD1D1034F70069BC94 /* PopoverRootView.swift */; }; 36 | D3ECECE31D1034F70069BC94 /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3ECECDE1D1034F70069BC94 /* Timer.swift */; }; 37 | /* End PBXBuildFile section */ 38 | 39 | /* Begin PBXCopyFilesBuildPhase section */ 40 | D338D4501D1141CC00BB86C1 /* CopyFiles */ = { 41 | isa = PBXCopyFilesBuildPhase; 42 | buildActionMask = 2147483647; 43 | dstPath = Contents/Library/LoginItems; 44 | dstSubfolderSpec = 1; 45 | files = ( 46 | D338D4511D1141E900BB86C1 /* LauncherApplication.app in CopyFiles */, 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXCopyFilesBuildPhase section */ 51 | 52 | /* Begin PBXFileReference section */ 53 | D338D4341D107E9D00BB86C1 /* NotificationsHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationsHandler.swift; sourceTree = ""; }; 54 | D338D4361D10842900BB86C1 /* CircleAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleAnimation.swift; sourceTree = ""; }; 55 | D338D4381D108FEE00BB86C1 /* Defaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Defaults.swift; sourceTree = ""; }; 56 | D338D43B1D113F5A00BB86C1 /* Pomodoro.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Pomodoro.entitlements; sourceTree = ""; }; 57 | D338D4401D1140B600BB86C1 /* LauncherApplication.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LauncherApplication.app; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | D338D4421D1140B600BB86C1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 59 | D338D4441D1140B600BB86C1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 60 | D338D4461D1140B600BB86C1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 61 | D338D4491D1140B600BB86C1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 62 | D338D44B1D1140B600BB86C1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63 | D338D44F1D1140E000BB86C1 /* LauncherApplication.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = LauncherApplication.entitlements; sourceTree = ""; }; 64 | D338D4521D11421500BB86C1 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; }; 65 | D338D4541D11422000BB86C1 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 66 | D338D4581D11AD7B00BB86C1 /* TasksViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TasksViewController.swift; path = Tasks/TasksViewController.swift; sourceTree = ""; }; 67 | D338D4591D11AD7B00BB86C1 /* TasksViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = TasksViewController.xib; path = Tasks/TasksViewController.xib; sourceTree = ""; }; 68 | D3E8E1961D13E5C80084B664 /* Tasks.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Tasks.plist; path = Tasks/Tasks.plist; sourceTree = ""; }; 69 | D3E8E1981D13E6D10084B664 /* TaskKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskKeys.swift; sourceTree = ""; }; 70 | D3E8E19B1D13F3550084B664 /* PlistManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlistManager.swift; sourceTree = ""; }; 71 | D3ECECBB1D1033BF0069BC94 /* Focusin.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Focusin.app; sourceTree = BUILT_PRODUCTS_DIR; }; 72 | D3ECECBE1D1033BF0069BC94 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 73 | D3ECECC01D1033C00069BC94 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 74 | D3ECECC31D1033C00069BC94 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 75 | D3ECECC51D1033C00069BC94 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 76 | D3ECECD01D1034860069BC94 /* AboutWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AboutWindowController.swift; path = About/AboutWindowController.swift; sourceTree = ""; }; 77 | D3ECECD11D1034860069BC94 /* AboutWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = AboutWindowController.xib; path = About/AboutWindowController.xib; sourceTree = ""; }; 78 | D3ECECD51D1034AC0069BC94 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PreferencesWindowController.swift; path = Preferences/PreferencesWindowController.swift; sourceTree = ""; }; 79 | D3ECECD61D1034AC0069BC94 /* PreferencesWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = PreferencesWindowController.xib; path = Preferences/PreferencesWindowController.xib; sourceTree = ""; }; 80 | D3ECECDA1D1034F70069BC94 /* EventMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventMonitor.swift; sourceTree = ""; }; 81 | D3ECECDB1D1034F70069BC94 /* PomodoroViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PomodoroViewController.swift; sourceTree = ""; }; 82 | D3ECECDC1D1034F70069BC94 /* PomodoroViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PomodoroViewController.xib; sourceTree = ""; }; 83 | D3ECECDD1D1034F70069BC94 /* PopoverRootView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopoverRootView.swift; sourceTree = ""; }; 84 | D3ECECDE1D1034F70069BC94 /* Timer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Timer.swift; sourceTree = ""; }; 85 | /* End PBXFileReference section */ 86 | 87 | /* Begin PBXFrameworksBuildPhase section */ 88 | D338D43D1D1140B600BB86C1 /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | D338D4551D11422000BB86C1 /* Cocoa.framework in Frameworks */, 93 | D338D4531D11421500BB86C1 /* ServiceManagement.framework in Frameworks */, 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | D3ECECB81D1033BF0069BC94 /* Frameworks */ = { 98 | isa = PBXFrameworksBuildPhase; 99 | buildActionMask = 2147483647; 100 | files = ( 101 | ); 102 | runOnlyForDeploymentPostprocessing = 0; 103 | }; 104 | /* End PBXFrameworksBuildPhase section */ 105 | 106 | /* Begin PBXGroup section */ 107 | D338D4411D1140B600BB86C1 /* LauncherApplication */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | D338D44F1D1140E000BB86C1 /* LauncherApplication.entitlements */, 111 | D338D4421D1140B600BB86C1 /* AppDelegate.swift */, 112 | D338D4441D1140B600BB86C1 /* ViewController.swift */, 113 | D338D4461D1140B600BB86C1 /* Assets.xcassets */, 114 | D338D4481D1140B600BB86C1 /* Main.storyboard */, 115 | D338D44B1D1140B600BB86C1 /* Info.plist */, 116 | ); 117 | path = LauncherApplication; 118 | sourceTree = ""; 119 | }; 120 | D338D4561D11422B00BB86C1 /* Frameworks */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | D338D4541D11422000BB86C1 /* Cocoa.framework */, 124 | D338D4521D11421500BB86C1 /* ServiceManagement.framework */, 125 | ); 126 | name = Frameworks; 127 | sourceTree = ""; 128 | }; 129 | D338D4571D11AD5700BB86C1 /* Task */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | D3E8E19B1D13F3550084B664 /* PlistManager.swift */, 133 | D338D4581D11AD7B00BB86C1 /* TasksViewController.swift */, 134 | D338D4591D11AD7B00BB86C1 /* TasksViewController.xib */, 135 | D3E8E1961D13E5C80084B664 /* Tasks.plist */, 136 | D3E8E1981D13E6D10084B664 /* TaskKeys.swift */, 137 | ); 138 | name = Task; 139 | sourceTree = ""; 140 | }; 141 | D3ECECB21D1033BF0069BC94 = { 142 | isa = PBXGroup; 143 | children = ( 144 | D338D4561D11422B00BB86C1 /* Frameworks */, 145 | D338D43B1D113F5A00BB86C1 /* Pomodoro.entitlements */, 146 | D3ECECBD1D1033BF0069BC94 /* Focusin */, 147 | D338D4411D1140B600BB86C1 /* LauncherApplication */, 148 | D3ECECBC1D1033BF0069BC94 /* Products */, 149 | ); 150 | sourceTree = ""; 151 | }; 152 | D3ECECBC1D1033BF0069BC94 /* Products */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | D3ECECBB1D1033BF0069BC94 /* Focusin.app */, 156 | D338D4401D1140B600BB86C1 /* LauncherApplication.app */, 157 | ); 158 | name = Products; 159 | sourceTree = ""; 160 | }; 161 | D3ECECBD1D1033BF0069BC94 /* Focusin */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | D338D4571D11AD5700BB86C1 /* Task */, 165 | D3ECECC01D1033C00069BC94 /* Assets.xcassets */, 166 | D3ECECCB1D1034430069BC94 /* About */, 167 | D3ECECD41D1034940069BC94 /* Preferences */, 168 | D3ECECDB1D1034F70069BC94 /* PomodoroViewController.swift */, 169 | D3ECECDC1D1034F70069BC94 /* PomodoroViewController.xib */, 170 | D3ECECDD1D1034F70069BC94 /* PopoverRootView.swift */, 171 | D3ECECDA1D1034F70069BC94 /* EventMonitor.swift */, 172 | D3ECECDE1D1034F70069BC94 /* Timer.swift */, 173 | D3ECECBE1D1033BF0069BC94 /* AppDelegate.swift */, 174 | D3ECECC21D1033C00069BC94 /* MainMenu.xib */, 175 | D3ECECC51D1033C00069BC94 /* Info.plist */, 176 | D338D4341D107E9D00BB86C1 /* NotificationsHandler.swift */, 177 | D338D4361D10842900BB86C1 /* CircleAnimation.swift */, 178 | D338D4381D108FEE00BB86C1 /* Defaults.swift */, 179 | ); 180 | path = Focusin; 181 | sourceTree = ""; 182 | }; 183 | D3ECECCB1D1034430069BC94 /* About */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | D3ECECD01D1034860069BC94 /* AboutWindowController.swift */, 187 | D3ECECD11D1034860069BC94 /* AboutWindowController.xib */, 188 | ); 189 | name = About; 190 | sourceTree = ""; 191 | }; 192 | D3ECECD41D1034940069BC94 /* Preferences */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | D3ECECD51D1034AC0069BC94 /* PreferencesWindowController.swift */, 196 | D3ECECD61D1034AC0069BC94 /* PreferencesWindowController.xib */, 197 | ); 198 | name = Preferences; 199 | sourceTree = ""; 200 | }; 201 | /* End PBXGroup section */ 202 | 203 | /* Begin PBXNativeTarget section */ 204 | D338D43F1D1140B600BB86C1 /* LauncherApplication */ = { 205 | isa = PBXNativeTarget; 206 | buildConfigurationList = D338D44C1D1140B600BB86C1 /* Build configuration list for PBXNativeTarget "LauncherApplication" */; 207 | buildPhases = ( 208 | D338D43C1D1140B600BB86C1 /* Sources */, 209 | D338D43D1D1140B600BB86C1 /* Frameworks */, 210 | D338D43E1D1140B600BB86C1 /* Resources */, 211 | D338D4501D1141CC00BB86C1 /* CopyFiles */, 212 | ); 213 | buildRules = ( 214 | ); 215 | dependencies = ( 216 | ); 217 | name = LauncherApplication; 218 | productName = LauncherApplication; 219 | productReference = D338D4401D1140B600BB86C1 /* LauncherApplication.app */; 220 | productType = "com.apple.product-type.application"; 221 | }; 222 | D3ECECBA1D1033BF0069BC94 /* Pomodoro */ = { 223 | isa = PBXNativeTarget; 224 | buildConfigurationList = D3ECECC81D1033C00069BC94 /* Build configuration list for PBXNativeTarget "Pomodoro" */; 225 | buildPhases = ( 226 | D3ECECB71D1033BF0069BC94 /* Sources */, 227 | D3ECECB81D1033BF0069BC94 /* Frameworks */, 228 | D3ECECB91D1033BF0069BC94 /* Resources */, 229 | ); 230 | buildRules = ( 231 | ); 232 | dependencies = ( 233 | ); 234 | name = Pomodoro; 235 | productName = Pomodoro; 236 | productReference = D3ECECBB1D1033BF0069BC94 /* Focusin.app */; 237 | productType = "com.apple.product-type.application"; 238 | }; 239 | /* End PBXNativeTarget section */ 240 | 241 | /* Begin PBXProject section */ 242 | D3ECECB31D1033BF0069BC94 /* Project object */ = { 243 | isa = PBXProject; 244 | attributes = { 245 | LastSwiftUpdateCheck = 0730; 246 | LastUpgradeCheck = 0820; 247 | ORGANIZATIONNAME = "Alberto Quesada Aranda"; 248 | TargetAttributes = { 249 | D338D43F1D1140B600BB86C1 = { 250 | CreatedOnToolsVersion = 7.3.1; 251 | DevelopmentTeam = SS2K3Q6K3Q; 252 | SystemCapabilities = { 253 | com.apple.Sandbox = { 254 | enabled = 1; 255 | }; 256 | }; 257 | }; 258 | D3ECECBA1D1033BF0069BC94 = { 259 | CreatedOnToolsVersion = 7.3.1; 260 | DevelopmentTeam = SS2K3Q6K3Q; 261 | LastSwiftMigration = 0810; 262 | SystemCapabilities = { 263 | com.apple.Sandbox = { 264 | enabled = 1; 265 | }; 266 | }; 267 | }; 268 | }; 269 | }; 270 | buildConfigurationList = D3ECECB61D1033BF0069BC94 /* Build configuration list for PBXProject "Focusin" */; 271 | compatibilityVersion = "Xcode 3.2"; 272 | developmentRegion = English; 273 | hasScannedForEncodings = 0; 274 | knownRegions = ( 275 | en, 276 | Base, 277 | ); 278 | mainGroup = D3ECECB21D1033BF0069BC94; 279 | productRefGroup = D3ECECBC1D1033BF0069BC94 /* Products */; 280 | projectDirPath = ""; 281 | projectRoot = ""; 282 | targets = ( 283 | D3ECECBA1D1033BF0069BC94 /* Pomodoro */, 284 | D338D43F1D1140B600BB86C1 /* LauncherApplication */, 285 | ); 286 | }; 287 | /* End PBXProject section */ 288 | 289 | /* Begin PBXResourcesBuildPhase section */ 290 | D338D43E1D1140B600BB86C1 /* Resources */ = { 291 | isa = PBXResourcesBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | D338D4471D1140B600BB86C1 /* Assets.xcassets in Resources */, 295 | D338D44A1D1140B600BB86C1 /* Main.storyboard in Resources */, 296 | ); 297 | runOnlyForDeploymentPostprocessing = 0; 298 | }; 299 | D3ECECB91D1033BF0069BC94 /* Resources */ = { 300 | isa = PBXResourcesBuildPhase; 301 | buildActionMask = 2147483647; 302 | files = ( 303 | D3E8E19A1D13EB570084B664 /* Tasks.plist in Resources */, 304 | D3ECECD31D1034860069BC94 /* AboutWindowController.xib in Resources */, 305 | D3ECECC11D1033C00069BC94 /* Assets.xcassets in Resources */, 306 | D3ECECE11D1034F70069BC94 /* PomodoroViewController.xib in Resources */, 307 | D3ECECD81D1034AC0069BC94 /* PreferencesWindowController.xib in Resources */, 308 | D3ECECC41D1033C00069BC94 /* MainMenu.xib in Resources */, 309 | D338D45B1D11AD7B00BB86C1 /* TasksViewController.xib in Resources */, 310 | ); 311 | runOnlyForDeploymentPostprocessing = 0; 312 | }; 313 | /* End PBXResourcesBuildPhase section */ 314 | 315 | /* Begin PBXSourcesBuildPhase section */ 316 | D338D43C1D1140B600BB86C1 /* Sources */ = { 317 | isa = PBXSourcesBuildPhase; 318 | buildActionMask = 2147483647; 319 | files = ( 320 | D338D4451D1140B600BB86C1 /* ViewController.swift in Sources */, 321 | D338D4431D1140B600BB86C1 /* AppDelegate.swift in Sources */, 322 | ); 323 | runOnlyForDeploymentPostprocessing = 0; 324 | }; 325 | D3ECECB71D1033BF0069BC94 /* Sources */ = { 326 | isa = PBXSourcesBuildPhase; 327 | buildActionMask = 2147483647; 328 | files = ( 329 | D3ECECD71D1034AC0069BC94 /* PreferencesWindowController.swift in Sources */, 330 | D3ECECBF1D1033BF0069BC94 /* AppDelegate.swift in Sources */, 331 | D338D45A1D11AD7B00BB86C1 /* TasksViewController.swift in Sources */, 332 | D338D4371D10842900BB86C1 /* CircleAnimation.swift in Sources */, 333 | D3E8E1991D13E6D10084B664 /* TaskKeys.swift in Sources */, 334 | D3ECECE31D1034F70069BC94 /* Timer.swift in Sources */, 335 | D3ECECE21D1034F70069BC94 /* PopoverRootView.swift in Sources */, 336 | D3ECECE01D1034F70069BC94 /* PomodoroViewController.swift in Sources */, 337 | D338D4351D107E9D00BB86C1 /* NotificationsHandler.swift in Sources */, 338 | D338D4391D108FEE00BB86C1 /* Defaults.swift in Sources */, 339 | D3ECECD21D1034860069BC94 /* AboutWindowController.swift in Sources */, 340 | D3ECECDF1D1034F70069BC94 /* EventMonitor.swift in Sources */, 341 | D3E8E19C1D13F3550084B664 /* PlistManager.swift in Sources */, 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | }; 345 | /* End PBXSourcesBuildPhase section */ 346 | 347 | /* Begin PBXVariantGroup section */ 348 | D338D4481D1140B600BB86C1 /* Main.storyboard */ = { 349 | isa = PBXVariantGroup; 350 | children = ( 351 | D338D4491D1140B600BB86C1 /* Base */, 352 | ); 353 | name = Main.storyboard; 354 | sourceTree = ""; 355 | }; 356 | D3ECECC21D1033C00069BC94 /* MainMenu.xib */ = { 357 | isa = PBXVariantGroup; 358 | children = ( 359 | D3ECECC31D1033C00069BC94 /* Base */, 360 | ); 361 | name = MainMenu.xib; 362 | sourceTree = ""; 363 | }; 364 | /* End PBXVariantGroup section */ 365 | 366 | /* Begin XCBuildConfiguration section */ 367 | D338D44D1D1140B600BB86C1 /* Debug */ = { 368 | isa = XCBuildConfiguration; 369 | buildSettings = { 370 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 371 | CODE_SIGN_ENTITLEMENTS = LauncherApplication/LauncherApplication.entitlements; 372 | CODE_SIGN_IDENTITY = "Mac Developer"; 373 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; 374 | COMBINE_HIDPI_IMAGES = YES; 375 | INFOPLIST_FILE = LauncherApplication/Info.plist; 376 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 377 | PRODUCT_BUNDLE_IDENTIFIER = com.albertoquesada.LauncherApplication; 378 | PRODUCT_NAME = "$(TARGET_NAME)"; 379 | PROVISIONING_PROFILE = ""; 380 | SKIP_INSTALL = YES; 381 | }; 382 | name = Debug; 383 | }; 384 | D338D44E1D1140B600BB86C1 /* Release */ = { 385 | isa = XCBuildConfiguration; 386 | buildSettings = { 387 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 388 | CODE_SIGN_ENTITLEMENTS = LauncherApplication/LauncherApplication.entitlements; 389 | CODE_SIGN_IDENTITY = "Mac Developer"; 390 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; 391 | COMBINE_HIDPI_IMAGES = YES; 392 | INFOPLIST_FILE = LauncherApplication/Info.plist; 393 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 394 | PRODUCT_BUNDLE_IDENTIFIER = com.albertoquesada.LauncherApplication; 395 | PRODUCT_NAME = "$(TARGET_NAME)"; 396 | PROVISIONING_PROFILE = ""; 397 | SKIP_INSTALL = YES; 398 | }; 399 | name = Release; 400 | }; 401 | D3ECECC61D1033C00069BC94 /* Debug */ = { 402 | isa = XCBuildConfiguration; 403 | buildSettings = { 404 | ALWAYS_SEARCH_USER_PATHS = NO; 405 | CLANG_ANALYZER_NONNULL = YES; 406 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 407 | CLANG_CXX_LIBRARY = "libc++"; 408 | CLANG_ENABLE_MODULES = YES; 409 | CLANG_ENABLE_OBJC_ARC = YES; 410 | CLANG_WARN_BOOL_CONVERSION = YES; 411 | CLANG_WARN_CONSTANT_CONVERSION = YES; 412 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 413 | CLANG_WARN_EMPTY_BODY = YES; 414 | CLANG_WARN_ENUM_CONVERSION = YES; 415 | CLANG_WARN_INFINITE_RECURSION = YES; 416 | CLANG_WARN_INT_CONVERSION = YES; 417 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 418 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 419 | CLANG_WARN_UNREACHABLE_CODE = YES; 420 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 421 | CODE_SIGN_IDENTITY = "-"; 422 | COPY_PHASE_STRIP = NO; 423 | DEBUG_INFORMATION_FORMAT = dwarf; 424 | ENABLE_STRICT_OBJC_MSGSEND = YES; 425 | ENABLE_TESTABILITY = YES; 426 | GCC_C_LANGUAGE_STANDARD = gnu99; 427 | GCC_DYNAMIC_NO_PIC = NO; 428 | GCC_NO_COMMON_BLOCKS = YES; 429 | GCC_OPTIMIZATION_LEVEL = 0; 430 | GCC_PREPROCESSOR_DEFINITIONS = ( 431 | "DEBUG=1", 432 | "$(inherited)", 433 | ); 434 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 435 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 436 | GCC_WARN_UNDECLARED_SELECTOR = YES; 437 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 438 | GCC_WARN_UNUSED_FUNCTION = YES; 439 | GCC_WARN_UNUSED_VARIABLE = YES; 440 | MACOSX_DEPLOYMENT_TARGET = 10.11; 441 | MTL_ENABLE_DEBUG_INFO = YES; 442 | ONLY_ACTIVE_ARCH = YES; 443 | SDKROOT = macosx; 444 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 445 | }; 446 | name = Debug; 447 | }; 448 | D3ECECC71D1033C00069BC94 /* Release */ = { 449 | isa = XCBuildConfiguration; 450 | buildSettings = { 451 | ALWAYS_SEARCH_USER_PATHS = NO; 452 | CLANG_ANALYZER_NONNULL = YES; 453 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 454 | CLANG_CXX_LIBRARY = "libc++"; 455 | CLANG_ENABLE_MODULES = YES; 456 | CLANG_ENABLE_OBJC_ARC = YES; 457 | CLANG_WARN_BOOL_CONVERSION = YES; 458 | CLANG_WARN_CONSTANT_CONVERSION = YES; 459 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 460 | CLANG_WARN_EMPTY_BODY = YES; 461 | CLANG_WARN_ENUM_CONVERSION = YES; 462 | CLANG_WARN_INFINITE_RECURSION = YES; 463 | CLANG_WARN_INT_CONVERSION = YES; 464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 465 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 466 | CLANG_WARN_UNREACHABLE_CODE = YES; 467 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 468 | CODE_SIGN_IDENTITY = "-"; 469 | COPY_PHASE_STRIP = NO; 470 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 471 | ENABLE_NS_ASSERTIONS = NO; 472 | ENABLE_STRICT_OBJC_MSGSEND = YES; 473 | GCC_C_LANGUAGE_STANDARD = gnu99; 474 | GCC_NO_COMMON_BLOCKS = YES; 475 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 476 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 477 | GCC_WARN_UNDECLARED_SELECTOR = YES; 478 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 479 | GCC_WARN_UNUSED_FUNCTION = YES; 480 | GCC_WARN_UNUSED_VARIABLE = YES; 481 | MACOSX_DEPLOYMENT_TARGET = 10.11; 482 | MTL_ENABLE_DEBUG_INFO = NO; 483 | SDKROOT = macosx; 484 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 485 | }; 486 | name = Release; 487 | }; 488 | D3ECECC91D1033C00069BC94 /* Debug */ = { 489 | isa = XCBuildConfiguration; 490 | buildSettings = { 491 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 492 | CODE_SIGN_ENTITLEMENTS = Pomodoro.entitlements; 493 | CODE_SIGN_IDENTITY = "Mac Developer"; 494 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; 495 | COMBINE_HIDPI_IMAGES = YES; 496 | DEVELOPMENT_TEAM = SS2K3Q6K3Q; 497 | INFOPLIST_FILE = "$(SRCROOT)/Focusin/Info.plist"; 498 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 499 | MACOSX_DEPLOYMENT_TARGET = 10.10; 500 | PRODUCT_BUNDLE_IDENTIFIER = com.albertoquesada.Focusin; 501 | PRODUCT_NAME = Focusin; 502 | PROVISIONING_PROFILE = ""; 503 | SWIFT_VERSION = 3.0; 504 | }; 505 | name = Debug; 506 | }; 507 | D3ECECCA1D1033C00069BC94 /* Release */ = { 508 | isa = XCBuildConfiguration; 509 | buildSettings = { 510 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 511 | CODE_SIGN_ENTITLEMENTS = Pomodoro.entitlements; 512 | CODE_SIGN_IDENTITY = "Mac Developer"; 513 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; 514 | COMBINE_HIDPI_IMAGES = YES; 515 | DEVELOPMENT_TEAM = SS2K3Q6K3Q; 516 | INFOPLIST_FILE = "$(SRCROOT)/Focusin/Info.plist"; 517 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 518 | MACOSX_DEPLOYMENT_TARGET = 10.10; 519 | PRODUCT_BUNDLE_IDENTIFIER = com.albertoquesada.Focusin; 520 | PRODUCT_NAME = Focusin; 521 | PROVISIONING_PROFILE = ""; 522 | SWIFT_VERSION = 3.0; 523 | }; 524 | name = Release; 525 | }; 526 | /* End XCBuildConfiguration section */ 527 | 528 | /* Begin XCConfigurationList section */ 529 | D338D44C1D1140B600BB86C1 /* Build configuration list for PBXNativeTarget "LauncherApplication" */ = { 530 | isa = XCConfigurationList; 531 | buildConfigurations = ( 532 | D338D44D1D1140B600BB86C1 /* Debug */, 533 | D338D44E1D1140B600BB86C1 /* Release */, 534 | ); 535 | defaultConfigurationIsVisible = 0; 536 | defaultConfigurationName = Release; 537 | }; 538 | D3ECECB61D1033BF0069BC94 /* Build configuration list for PBXProject "Focusin" */ = { 539 | isa = XCConfigurationList; 540 | buildConfigurations = ( 541 | D3ECECC61D1033C00069BC94 /* Debug */, 542 | D3ECECC71D1033C00069BC94 /* Release */, 543 | ); 544 | defaultConfigurationIsVisible = 0; 545 | defaultConfigurationName = Release; 546 | }; 547 | D3ECECC81D1033C00069BC94 /* Build configuration list for PBXNativeTarget "Pomodoro" */ = { 548 | isa = XCConfigurationList; 549 | buildConfigurations = ( 550 | D3ECECC91D1033C00069BC94 /* Debug */, 551 | D3ECECCA1D1033C00069BC94 /* Release */, 552 | ); 553 | defaultConfigurationIsVisible = 0; 554 | defaultConfigurationName = Release; 555 | }; 556 | /* End XCConfigurationList section */ 557 | }; 558 | rootObject = D3ECECB31D1033BF0069BC94 /* Project object */; 559 | } 560 | -------------------------------------------------------------------------------- /Focusin.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Focusin.xcodeproj/xcuserdata/alberto.xcuserdatad/xcschemes/Pomodoro.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Focusin.xcodeproj/xcuserdata/alberto.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | LauncherApplication.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | Pomodoro.xcscheme 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | D338D43F1D1140B600BB86C1 21 | 22 | primary 23 | 24 | 25 | D3ECECBA1D1033BF0069BC94 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Focusin/About/AboutWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutWindowController.swift 3 | // Pomodoro 4 | // 5 | // Created by Alberto Quesada Aranda on 13/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class AboutWindowController: NSWindowController { 12 | 13 | override func windowDidLoad() { 14 | super.windowDidLoad() 15 | self.window?.center() 16 | self.window?.makeKeyAndOrderFront(nil) 17 | NSApp.activate(ignoringOtherApps: true) 18 | } 19 | 20 | override var windowNibName : String! { 21 | return "AboutWindowController" 22 | } 23 | 24 | @IBAction func openIcons8(_ sender: AnyObject) { 25 | NSWorkspace.shared().open(URL(string: "http://icons8.com")!) 26 | } 27 | 28 | @IBAction func openAlberto(_ sender: AnyObject) { 29 | NSWorkspace.shared().open(URL(string: "http://albertoquesada.com")!) 30 | } 31 | 32 | @IBAction func getCode(_ sender: AnyObject) { 33 | NSWorkspace.shared().open(URL(string: "https://github.com/albertoqa/Focusin")!) 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Focusin/About/AboutWindowController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 78 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /Focusin/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Pomodoro 4 | // 5 | // Created by Alberto Quesada Aranda on 13/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import ServiceManagement 11 | 12 | @NSApplicationMain 13 | class AppDelegate: NSObject, NSApplicationDelegate { 14 | 15 | @IBOutlet weak var window: NSWindow! // this window will be hidden 16 | 17 | var eventMonitor: EventMonitor? // monitor if the user click outside of the app 18 | 19 | let menu = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength) 20 | let popover = NSPopover() 21 | 22 | let barIcon = "goal-1" 23 | let timeFormat = "%d:%02d" 24 | 25 | func applicationDidFinishLaunching(_ notification: Notification) { 26 | 27 | let defaultPomodoroDuration = 25 * 60 28 | let defaultLongBreakDuration = 15 * 5 29 | let defaultShortBreakDuration = 5 * 60 30 | let defaultTargetPomodoros = 8 31 | let defaultLongBreakAfterXPomodoros = 4 32 | 33 | /*NSUserDefaults.standardUserDefaults().setInteger(3, forKey: Defaults.pomodoroKey) 34 | NSUserDefaults.standardUserDefaults().setInteger(3, forKey: Defaults.shortBreakKey) 35 | NSUserDefaults.standardUserDefaults().setInteger(5, forKey: Defaults.longBreakKey) 36 | NSUserDefaults.standardUserDefaults().setInteger(2, forKey: Defaults.longBreakAfterXPomodoros) 37 | NSUserDefaults.standardUserDefaults().setInteger(4, forKey: Defaults.targetKey)*/ 38 | 39 | /* On first time launch set the default values */ 40 | if(UserDefaults.standard.string(forKey: Defaults.pomodoroKey) == nil) { 41 | UserDefaults.standard.set(defaultPomodoroDuration, forKey: Defaults.pomodoroKey) 42 | UserDefaults.standard.set(defaultLongBreakDuration, forKey: Defaults.longBreakKey) 43 | UserDefaults.standard.set(defaultShortBreakDuration, forKey: Defaults.shortBreakKey) 44 | UserDefaults.standard.set(defaultLongBreakAfterXPomodoros, forKey: Defaults.longBreakAfterXPomodoros) 45 | UserDefaults.standard.set(defaultTargetPomodoros, forKey: Defaults.targetKey) 46 | UserDefaults.standard.set(NSOnState, forKey: Defaults.showTimeKey) 47 | UserDefaults.standard.set(NSOnState, forKey: Defaults.showNotificationsKey) 48 | UserDefaults.standard.set(NSOffState, forKey: Defaults.startAtLogin) 49 | } 50 | 51 | let launcherAppIdentifier = "com.albertoquesada.LauncherApplication" 52 | SMLoginItemSetEnabled(launcherAppIdentifier as CFString, UserDefaults.standard.integer(forKey: Defaults.startAtLogin) == NSOnState) 53 | 54 | var startedAtLogin = false 55 | for app in NSWorkspace.shared().runningApplications { 56 | if(app.bundleIdentifier == launcherAppIdentifier) { 57 | startedAtLogin = true 58 | break 59 | } 60 | } 61 | 62 | if startedAtLogin { 63 | DistributedNotificationCenter.default().post(name: NSNotification.Name.init(rawValue: "killme"), object: Bundle.main.bundleIdentifier!) 64 | } 65 | 66 | // Set the icon for the menu bar 67 | let button = menu.button 68 | let icon = NSImage(named: barIcon) 69 | icon?.size.height = 18 70 | icon?.size.width = 18 71 | icon?.isTemplate = true // normal and dark mode 72 | button!.image = icon 73 | button!.imagePosition = NSCellImagePosition.imageLeft 74 | button!.action = #selector(AppDelegate.togglePopover(_:)) 75 | 76 | // Show time in menu bar only if user wants it 77 | if(UserDefaults.standard.integer(forKey: Defaults.showTimeKey) == NSOnState) { 78 | let pomodoroDefaultDuration = UserDefaults.standard.integer(forKey: Defaults.pomodoroKey) 79 | button!.title = String(format: timeFormat, pomodoroDefaultDuration/60, pomodoroDefaultDuration%60) 80 | } 81 | 82 | popover.contentViewController = PomodoroViewController(nibName: "PomodoroViewController", bundle: nil, button: button!, popover: popover) 83 | 84 | eventMonitor = EventMonitor(mask: .leftMouseDown) { [unowned self] event in 85 | if self.popover.isShown { 86 | self.closePopover(event) 87 | } 88 | } 89 | eventMonitor?.start() 90 | } 91 | 92 | /* Show popover when click on menu bar button */ 93 | func showPopover(_ sender: AnyObject?) { 94 | NSApp.activate(ignoringOtherApps: true) 95 | 96 | if let button = menu.button { 97 | popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY) 98 | } 99 | 100 | eventMonitor?.start() 101 | } 102 | 103 | /* Close popover when click outside of the view */ 104 | func closePopover(_ sender: AnyObject?) { 105 | popover.performClose(sender) 106 | eventMonitor?.stop() 107 | } 108 | 109 | /* Toggle the popover visibility */ 110 | func togglePopover(_ sender: AnyObject?) { 111 | if popover.isShown { 112 | closePopover(sender) 113 | } else { 114 | showPopover(sender) 115 | } 116 | } 117 | 118 | func applicationWillTerminate(_ aNotification: Notification) { 119 | // Insert code here to tear down your application 120 | } 121 | 122 | } 123 | 124 | -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/128x128.imageset/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/128x128.imageset/128x128.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/128x128.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "128x128.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/256x256.imageset/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/256x256.imageset/256x256.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/256x256.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "256x256.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/AppIcon.appiconset/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/AppIcon.appiconset/1024x1024.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/AppIcon.appiconset/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/AppIcon.appiconset/128x128.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/AppIcon.appiconset/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/AppIcon.appiconset/16x16.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/AppIcon.appiconset/256x256-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/AppIcon.appiconset/256x256-1.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/AppIcon.appiconset/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/AppIcon.appiconset/256x256.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/AppIcon.appiconset/32x32-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/AppIcon.appiconset/32x32-1.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/AppIcon.appiconset/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/AppIcon.appiconset/32x32.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/AppIcon.appiconset/512x512-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/AppIcon.appiconset/512x512-1.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/AppIcon.appiconset/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/AppIcon.appiconset/512x512.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/AppIcon.appiconset/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/AppIcon.appiconset/64x64.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "32x32-1.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "64x64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "256x256-1.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "512x512-1.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "1024x1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/Hacking.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Hacking.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/Hacking.imageset/Hacking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/Hacking.imageset/Hacking.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "back.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/back.imageset/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/back.imageset/back.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/cancel-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cancel.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/cancel-1.imageset/cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/cancel-1.imageset/cancel.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/cancel.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "cancel.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/cancel.imageset/cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/cancel.imageset/cancel.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/checked.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "checked.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/checked.imageset/checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/checked.imageset/checked.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/circle_thin.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "circle_thin.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/circle_thin.imageset/circle_thin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/circle_thin.imageset/circle_thin.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/circled_left_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "circled_left_2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/circled_left_2.imageset/circled_left_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/circled_left_2.imageset/circled_left_2.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/circled_left_2_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "circled_left_2_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/circled_left_2_filled.imageset/circled_left_2_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/circled_left_2_filled.imageset/circled_left_2_filled.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "delete.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/delete.imageset/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/delete.imageset/delete.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/delete_sign-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "delete_sign.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/delete_sign-1.imageset/delete_sign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/delete_sign-1.imageset/delete_sign.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/delete_sign.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "delete_sign.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/delete_sign.imageset/delete_sign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/delete_sign.imageset/delete_sign.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/github.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "github.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/github.imageset/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/github.imageset/github.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/github_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "github_2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/github_2.imageset/github_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/github_2.imageset/github_2.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/goal-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "goal.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/goal-1.imageset/goal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/goal-1.imageset/goal.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/goal.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "goal.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/goal.imageset/goal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/goal.imageset/goal.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/goal_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "goal_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/goal_filled.imageset/goal_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/goal_filled.imageset/goal_filled.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/icons8_logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8_logo.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/icons8_logo.imageset/icons8_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/icons8_logo.imageset/icons8_logo.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/left_round.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "left_round.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/left_round.imageset/left_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/left_round.imageset/left_round.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/minus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "minus.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/minus.imageset/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/minus.imageset/minus.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/ok-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ok.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/ok-1.imageset/ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/ok-1.imageset/ok.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/ok.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ok.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/ok.imageset/ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/ok.imageset/ok.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/pause-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pause.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/pause-1.imageset/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/pause-1.imageset/pause.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/pause-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pause.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/pause-2.imageset/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/pause-2.imageset/pause.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/pause-3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pause.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/pause-3.imageset/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/pause-3.imageset/pause.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/pause.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pause.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/pause.imageset/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/pause.imageset/pause.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/pause_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pause_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/pause_filled.imageset/pause_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/pause_filled.imageset/pause_filled.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/play-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "play.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/play-1.imageset/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/play-1.imageset/play.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/play-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "play.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/play-2.imageset/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/play-2.imageset/play.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/play-3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "play.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/play-3.imageset/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/play-3.imageset/play.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/play.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "play.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/play.imageset/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/play.imageset/play.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/play_round.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "play_round.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/play_round.imageset/play_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/play_round.imageset/play_round.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/settings-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "settings.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/settings-1.imageset/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/settings-1.imageset/settings.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/settings-m.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "horizontal_settings_mixer.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/settings-m.imageset/horizontal_settings_mixer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/settings-m.imageset/horizontal_settings_mixer.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/settings-m1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "horizontal_settings_mixer_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/settings-m1.imageset/horizontal_settings_mixer_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/settings-m1.imageset/horizontal_settings_mixer_filled.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/settings.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "settings.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/settings.imageset/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/settings.imageset/settings.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/sofa.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sofa.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/sofa.imageset/sofa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/sofa.imageset/sofa.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/sofa_filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sofa_filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/sofa_filled.imageset/sofa_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/sofa_filled.imageset/sofa_filled.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/timer-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "timer.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/timer-1.imageset/timer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/timer-1.imageset/timer.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/timer-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "timer.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/timer-2.imageset/timer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/timer-2.imageset/timer.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/timer-3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "timer.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/timer-3.imageset/timer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/timer-3.imageset/timer.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/timer.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "timer.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/timer.imageset/timer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/timer.imageset/timer.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/todo_list-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "todo_list.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/todo_list-1.imageset/todo_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/todo_list-1.imageset/todo_list.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/todo_list-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "todo_list.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/todo_list-2.imageset/todo_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/todo_list-2.imageset/todo_list.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/todo_list.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "todo_list.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/todo_list.imageset/todo_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/todo_list.imageset/todo_list.png -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/tomato.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tomato.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Focusin/Assets.xcassets/tomato.imageset/tomato.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/Focusin/Assets.xcassets/tomato.imageset/tomato.png -------------------------------------------------------------------------------- /Focusin/CircleAnimation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CircleAnimation.swift 3 | // Focusin 4 | // 5 | // Created by Alberto Quesada Aranda on 14/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | /* In this app there are two circles: one for the time and one for the target pomodoros to complete */ 12 | enum Circles { 13 | case time, target 14 | } 15 | 16 | /* Add two layers and animations to the pomodoro view */ 17 | open class CircleAnimation { 18 | 19 | let defaults = UserDefaults.standard 20 | 21 | // Time circle (big) 22 | let timeLeftShapeLayer = CAShapeLayer() 23 | let bgTimeLeftShapeLayer = CAShapeLayer() 24 | var strokeTimeIt = CABasicAnimation(keyPath: "strokeEnd") 25 | let radiusBig: CGFloat = 65 26 | let widthBig: CGFloat = 5 27 | 28 | // Target circle (small) 29 | let targetShapeLayer = CAShapeLayer() 30 | let bgTargetShapeLayer = CAShapeLayer() 31 | let strokeTargetIt = CABasicAnimation(keyPath: "strokeEnd") 32 | let radiusSmall: CGFloat = 25 33 | let widthSmall: CGFloat = 2 34 | 35 | // Short break circle 36 | let shortBreakShapeLayer = CAShapeLayer() 37 | 38 | // Long break circle 39 | let longBreakShapeLayer = CAShapeLayer() 40 | 41 | let mainView: PopoverRootView // pomodoro view 42 | 43 | let strokeStartValue: Double = 0.0 44 | let strokeToValue: Double = 1.0 45 | 46 | let orangeFull = NSColor.init(red: 0.929, green:0.416, blue:0.353, alpha:1).cgColor 47 | let orangeAplha = NSColor.init(red: 0.929, green:0.416, blue:0.353, alpha:0.5).cgColor 48 | let greenFull = NSColor.init(red: 0.608, green:0.757, blue:0.737, alpha:1).cgColor 49 | let greenAlpha = NSColor.init(red: 0.608, green:0.757, blue:0.737, alpha:0.5).cgColor 50 | let gray = NSColor.init(red: 0.551, green:0.551, blue:0.551, alpha:1).cgColor 51 | 52 | // startButton is the position of the Time Circle, fullPomodoros is the position of the Target circle 53 | init(popoverRootView: PopoverRootView, startButton: NSButton, fullPomodoros: NSTextField, shortBreak: NSButton, longBreak: NSButton) { 54 | self.mainView = popoverRootView 55 | 56 | // Animation circle for the current timer 57 | drawBgShape(bgTimeLeftShapeLayer, center: CGPoint(x: startButton.frame.midX, y: startButton.frame.midY), 58 | radius: radiusBig, lineWidth: widthBig) 59 | drawShape(timeLeftShapeLayer, center: CGPoint(x: startButton.frame.midX, y: startButton.frame.midY), 60 | radius: radiusBig, lineWidth: widthBig) 61 | 62 | strokeTimeIt.fromValue = strokeStartValue 63 | strokeTimeIt.toValue = strokeToValue 64 | strokeTimeIt.duration = defaults.double(forKey: Defaults.pomodoroKey)+1 65 | strokeTargetIt.isRemovedOnCompletion = true 66 | pauseLayer(Circles.time) 67 | timeLeftShapeLayer.add(strokeTimeIt, forKey: "timeLeft") 68 | 69 | // Animation circle for the target pomodoros 70 | drawBgShape(bgTargetShapeLayer, center: CGPoint(x: fullPomodoros.frame.midX, y: fullPomodoros.frame.midY), 71 | radius: radiusSmall, lineWidth: widthSmall) 72 | drawShape(targetShapeLayer, center: CGPoint(x: fullPomodoros.frame.midX, y: fullPomodoros.frame.midY), 73 | radius: radiusSmall, lineWidth: widthSmall) 74 | 75 | strokeTargetIt.fromValue = strokeStartValue 76 | strokeTargetIt.toValue = strokeToValue 77 | strokeTargetIt.duration = defaults.double(forKey: Defaults.targetKey)*defaults.double(forKey: Defaults.pomodoroKey) 78 | strokeTargetIt.isRemovedOnCompletion = false 79 | pauseLayer(Circles.target) 80 | //targetShapeLayer.addAnimation(strokeTargetIt, forKey: "target") 81 | 82 | // Static circle for short break 83 | drawCompleteCircleSpahe(shortBreakShapeLayer, center: CGPoint(x: shortBreak.frame.midX, y: shortBreak.frame.midY-3), 84 | radius: radiusSmall, lineWidth: widthSmall, color: greenFull) 85 | 86 | // Static circle for long break 87 | drawCompleteCircleSpahe(longBreakShapeLayer, center: CGPoint(x: longBreak.frame.midX, y: longBreak.frame.midY), 88 | radius: radiusSmall, lineWidth: widthSmall, color: gray) 89 | 90 | } 91 | 92 | /* Draw a complete static circle */ 93 | func drawCompleteCircleSpahe(_ layer: CAShapeLayer, center: CGPoint, radius: CGFloat, lineWidth: CGFloat, color: CGColor) { 94 | let bez = NSBezierPath() 95 | bez.appendArc(withCenter: center, radius: 96 | radius, startAngle: -90.degreesToRadians, endAngle: 360.degreesToRadians, clockwise: true) 97 | layer.path = bez.CGPath(forceClose: false) 98 | layer.strokeColor = color 99 | layer.fillColor = NSColor.clear.cgColor 100 | layer.lineWidth = lineWidth 101 | mainView.wantsLayer = true 102 | layer.zPosition = 1 103 | mainView.layer!.addSublayer(layer) 104 | } 105 | 106 | /* Draw a cricle on the given layer */ 107 | func drawBgShape(_ layer: CAShapeLayer, center: CGPoint, radius: CGFloat, lineWidth: CGFloat) { 108 | let bez = NSBezierPath() 109 | bez.appendArc(withCenter: center, radius: 110 | radius, startAngle: -90.degreesToRadians, endAngle: 270.degreesToRadians, clockwise: true) 111 | layer.path = bez.CGPath(forceClose: false) 112 | layer.strokeColor = orangeAplha 113 | layer.fillColor = NSColor.clear.cgColor 114 | layer.lineWidth = lineWidth 115 | mainView.wantsLayer = true 116 | layer.zPosition = 1 117 | mainView.layer!.addSublayer(layer) 118 | } 119 | 120 | /* Draw a circle over the previous circle to make the apparence of fill it */ 121 | func drawShape(_ layer: CAShapeLayer, center: CGPoint, radius: CGFloat, lineWidth: CGFloat) { 122 | let bez = NSBezierPath() 123 | bez.appendArc(withCenter: center, radius: 124 | radius, startAngle: -90.degreesToRadians, endAngle: 270.degreesToRadians, clockwise: true) 125 | layer.path = bez.CGPath(forceClose: false) 126 | layer.strokeColor = orangeFull 127 | layer.fillColor = NSColor.clear.cgColor 128 | layer.lineWidth = lineWidth 129 | layer.zPosition = 1 130 | mainView.layer!.addSublayer(layer) 131 | } 132 | 133 | /* Pause the animation for the given circle */ 134 | func pauseLayer(_ circle: Circles) { 135 | let layer = circle == Circles.time ? timeLeftShapeLayer : targetShapeLayer 136 | let pausedTime : CFTimeInterval = layer.convertTime(CACurrentMediaTime(), from: nil) 137 | layer.speed = 0.0 138 | layer.timeOffset = pausedTime 139 | } 140 | 141 | /* Resume the animation for the given circle */ 142 | func resumeLayer(_ circle: Circles) { 143 | let layer = circle == Circles.time ? timeLeftShapeLayer : targetShapeLayer 144 | let pausedTime = layer.timeOffset 145 | layer.speed = 1.0; 146 | layer.timeOffset = 0.0; 147 | layer.beginTime = 0.0; 148 | let timeSincePause : CFTimeInterval = layer.convertTime(CACurrentMediaTime(), from: nil) - pausedTime 149 | layer.beginTime = timeSincePause 150 | } 151 | 152 | /* Reset the animation for the given circle */ 153 | func resetLayer(_ circle: Circles) { 154 | let layer = circle == Circles.time ? timeLeftShapeLayer : targetShapeLayer 155 | layer.speed = 0.0; 156 | layer.timeOffset = 0.0; 157 | layer.beginTime = 0.0; 158 | } 159 | 160 | /* Restart (reset and start) the animation for the given circle */ 161 | func restartLayer(_ circle: Circles) { 162 | resetLayer(circle) 163 | resumeLayer(circle) 164 | } 165 | 166 | /* Set the color of the circle and the animation */ 167 | func setTimeLayerColor(_ isPomodoro: Bool) { 168 | if(isPomodoro) { 169 | timeLeftShapeLayer.strokeColor = orangeFull 170 | bgTimeLeftShapeLayer.strokeColor = orangeAplha 171 | } else { 172 | timeLeftShapeLayer.strokeColor = greenFull 173 | bgTimeLeftShapeLayer.strokeColor = greenAlpha 174 | } 175 | } 176 | 177 | /*func resetLastPomodoro() { 178 | let pausedTime = targetShapeLayer.timeOffset 179 | targetShapeLayer.speed = 1.0; 180 | targetShapeLayer.timeOffset = 0.0; 181 | targetShapeLayer.beginTime = 0.0; 182 | let timeSincePause : CFTimeInterval = targetShapeLayer.convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime 183 | //targetShapeLayer.beginTime = timeSincePause - (timeSincePause - (timeSincePause%defaults.doubleForKey("pomodoroDuration"))) 184 | }*/ 185 | 186 | 187 | /* Add a new animation for the time left. Remove previous animation before add the new one. */ 188 | func addTimeLeftAnimation(_ isPomodoro: Bool, isLongBreak: Bool) { 189 | if(timeLeftShapeLayer.animation(forKey: "timeLeft") != nil) { 190 | timeLeftShapeLayer.removeAnimation(forKey: "timeLeft") 191 | } 192 | 193 | if(isPomodoro) { 194 | strokeTimeIt.duration = defaults.double(forKey: Defaults.pomodoroKey)+1 195 | } else if(isLongBreak) { 196 | strokeTimeIt.duration = defaults.double(forKey: Defaults.longBreakKey)+1 197 | } else { 198 | strokeTimeIt.duration = defaults.double(forKey: Defaults.shortBreakKey)+1 199 | } 200 | timeLeftShapeLayer.add(strokeTimeIt, forKey: "timeLeft") 201 | } 202 | 203 | } 204 | 205 | extension Double { 206 | var degreesToRadians : CGFloat { 207 | return CGFloat(self) * CGFloat(M_PI) / 180.0 208 | } 209 | } 210 | 211 | extension NSBezierPath { 212 | func CGPath(forceClose:Bool) -> CGPath? { 213 | var cgPath:CGPath? = nil 214 | 215 | let numElements = self.elementCount 216 | if numElements > 0 { 217 | let newPath = CGMutablePath() 218 | var points = [CGPoint](repeating: .zero, count: 3) 219 | var bDidClosePath:Bool = true 220 | 221 | for i in 0 ..< numElements { 222 | let type = self.element(at: i, associatedPoints: &points) 223 | switch type { 224 | 225 | case .moveToBezierPathElement: 226 | newPath.move(to: points[0]) 227 | 228 | case .lineToBezierPathElement: 229 | newPath.addLine(to: points[0]) 230 | bDidClosePath = false 231 | 232 | case .curveToBezierPathElement: 233 | newPath.addCurve(to: points[2], control1: points[0], control2: points[1]) 234 | bDidClosePath = false 235 | 236 | case .closePathBezierPathElement: 237 | newPath.closeSubpath() 238 | bDidClosePath = true 239 | } 240 | 241 | if forceClose && !bDidClosePath { 242 | newPath.closeSubpath() 243 | } 244 | } 245 | cgPath = newPath.copy() 246 | } 247 | return cgPath 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /Focusin/Defaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultsManager.swift 3 | // Focusin 4 | // 5 | // Created by Alberto Quesada Aranda on 14/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Defaults { 12 | static let pomodoroKey = "pomodoroDuration" 13 | static let shortBreakKey = "shortBreakDuration" 14 | static let longBreakKey = "longBreakDuration" 15 | static let longBreakAfterXPomodoros = "longBreakAfterXPomodoros" 16 | static let targetKey = "targetPomodoros" 17 | static let showTimeKey = "showTimeInBar" 18 | static let showNotificationsKey = "showNotifications" 19 | static let startAtLogin = "startAtLogin" 20 | } 21 | -------------------------------------------------------------------------------- /Focusin/EventMonitor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventMonitor.swift 3 | // Pomodoro 4 | // 5 | // Created by Alberto Quesada Aranda on 13/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | /* Close the popover window when press outside of it */ 12 | open class EventMonitor { 13 | fileprivate var monitor: AnyObject? 14 | fileprivate let mask: NSEventMask 15 | fileprivate let handler: (NSEvent?) -> () 16 | 17 | public init(mask: NSEventMask, handler: @escaping (NSEvent?) -> ()) { 18 | self.mask = mask 19 | self.handler = handler 20 | } 21 | 22 | deinit { 23 | stop() 24 | } 25 | 26 | open func start() { 27 | monitor = NSEvent.addGlobalMonitorForEvents(matching: mask, handler: handler) as AnyObject? 28 | } 29 | 30 | open func stop() { 31 | if monitor != nil { 32 | NSEvent.removeMonitor(monitor!) 33 | monitor = nil 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Focusin/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 | 0.2 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSApplicationCategoryType 26 | public.app-category.productivity 27 | LSMinimumSystemVersion 28 | $(MACOSX_DEPLOYMENT_TARGET) 29 | LSUIElement 30 | 31 | NSHumanReadableCopyright 32 | Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 33 | NSMainNibFile 34 | MainMenu 35 | NSPrincipalClass 36 | NSApplication 37 | NSUserNotificationAlertStyle 38 | alert 39 | 40 | 41 | -------------------------------------------------------------------------------- /Focusin/NotificationsHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationsHandler.swift 3 | // Focusin 4 | // 5 | // Created by Alberto Quesada Aranda on 14/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Cocoa 11 | 12 | /* The delegate must implement the methods for control the action to perform for each notification */ 13 | protocol NotificationsDelegate { 14 | func handleNotificationAction(_ caller: Caller) 15 | func handleNotificationOther(_ caller: Caller) 16 | func handleNotificationOpenApp() 17 | } 18 | 19 | /* Who is invoking the notification? The point of the application who create the notification. */ 20 | enum Caller { 21 | case target, pomodoro, `break` 22 | } 23 | 24 | /* Create and handle interaction with user notifications */ 25 | open class NotificationsHandler: NSObject, NSUserNotificationCenterDelegate { 26 | 27 | var delegate: NotificationsDelegate? 28 | 29 | var caller: Caller = Caller.break 30 | var actionButtonPressed: Bool = false // allows to differenciate between the buttons of the notification 31 | 32 | override init() { 33 | super.init() 34 | NSUserNotificationCenter.default.delegate = self 35 | } 36 | 37 | /* Detect if the user interact with the notification - real action button */ 38 | open func userNotificationCenter(_ center: NSUserNotificationCenter, didActivate notification: NSUserNotification) { 39 | if(notification.activationType == NSUserNotification.ActivationType.actionButtonClicked) { 40 | self.handleNotifications(notification, isActionButton: true, openApp: false) 41 | } else { 42 | self.handleNotifications(notification, isActionButton: true, openApp: true) 43 | } 44 | } 45 | 46 | /* Show always the notification, even if the app is open */ 47 | open func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool { 48 | return true 49 | } 50 | 51 | /* Detect dismissed notification -> in this app that is considered as press the other button */ 52 | open func userNotificationCenter(_ center: NSUserNotificationCenter, didDeliver notification: NSUserNotification) { 53 | let priority = DispatchQueue.GlobalQueuePriority.default 54 | DispatchQueue.global(priority: priority).async { 55 | var notificationStillPresent = true 56 | while (notificationStillPresent) { 57 | Thread.sleep(forTimeInterval: 1) 58 | notificationStillPresent = false 59 | for deliveredNotification in NSUserNotificationCenter.default.deliveredNotifications { 60 | if deliveredNotification.identifier == notification.identifier { 61 | notificationStillPresent = true 62 | } 63 | } 64 | } 65 | DispatchQueue.main.async { 66 | self.handleNotifications(notification, isActionButton: false, openApp: false) 67 | } 68 | } 69 | } 70 | 71 | /* Clear all notifications from the app */ 72 | func removeAllNotifications() { 73 | actionButtonPressed = true // this is set so the notification is not handled 74 | NSUserNotificationCenter.default.removeAllDeliveredNotifications() 75 | } 76 | 77 | /* Show a new notification on the Notification Center */ 78 | func showNotification(_ title: String, text: String, actionTitle: String, otherTitle: String) -> Void { 79 | let notification = NSUserNotification() 80 | notification.title = title 81 | notification.informativeText = text 82 | notification.soundName = NSUserNotificationDefaultSoundName 83 | notification.actionButtonTitle = actionTitle 84 | notification.otherButtonTitle = otherTitle 85 | //notification.contentImage = NSImage(named: "goal") 86 | NSUserNotificationCenter.default.deliver(notification) 87 | } 88 | 89 | /* Handle the notifications */ 90 | func handleNotifications(_ notification: NSUserNotification, isActionButton: Bool, openApp: Bool) { 91 | if(isActionButton && !openApp) { 92 | actionButtonPressed = true 93 | handleNotificationAction(caller) 94 | } else if(openApp) { 95 | handleNotificationOpenApp() 96 | removeAllNotifications() 97 | } else if(!actionButtonPressed) { // TODO this is not working for the "Close" notifications 98 | actionButtonPressed = false 99 | handleNotificationOther(caller) 100 | } else { 101 | actionButtonPressed = false 102 | } 103 | } 104 | 105 | /* Notification action: button action */ 106 | func handleNotificationAction(_ caller: Caller) { 107 | delegate?.handleNotificationAction(caller) 108 | } 109 | 110 | /* Notification action: other action */ 111 | func handleNotificationOther(_ caller: Caller) { 112 | delegate?.handleNotificationOther(caller) 113 | } 114 | 115 | /* Close the notification and open the app */ 116 | func handleNotificationOpenApp() { 117 | delegate?.handleNotificationOpenApp() 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /Focusin/PlistManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlistManager.swift 3 | // PlistManagerExample 4 | // 5 | // Created by SANDOR NAGY on 27/05/16. 6 | // Copyright © 2016 Rebeloper. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let plistFileName:String = "Tasks" 12 | 13 | struct Plist { 14 | 15 | enum PlistError: Error { 16 | case fileNotWritten 17 | case fileDoesNotExist 18 | } 19 | 20 | let name:String 21 | 22 | var sourcePath:String? { 23 | guard let path = Bundle.main.path(forResource: name, ofType: "plist") else { return .none } 24 | return path 25 | } 26 | 27 | var destPath:String? { 28 | guard sourcePath != .none else { return .none } 29 | let dir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] 30 | return (dir as NSString).appendingPathComponent("\(name).plist") 31 | } 32 | 33 | init?(name:String) { 34 | 35 | self.name = name 36 | 37 | let fileManager = FileManager.default 38 | 39 | guard let source = sourcePath else { return nil } 40 | guard let destination = destPath else { return nil } 41 | guard fileManager.fileExists(atPath: source) else { return nil } 42 | 43 | if !fileManager.fileExists(atPath: destination) { 44 | 45 | do { 46 | try fileManager.copyItem(atPath: source, toPath: destination) 47 | } catch let error as NSError { 48 | print("[PlistManager] Unable to copy file. ERROR: \(error.localizedDescription)") 49 | return nil 50 | } 51 | } 52 | } 53 | 54 | func getValuesInPlistFile() -> NSDictionary?{ 55 | let fileManager = FileManager.default 56 | if fileManager.fileExists(atPath: destPath!) { 57 | guard let dict = NSDictionary(contentsOfFile: destPath!) else { return .none } 58 | return dict 59 | } else { 60 | return .none 61 | } 62 | } 63 | 64 | func getMutablePlistFile() -> NSMutableDictionary?{ 65 | let fileManager = FileManager.default 66 | if fileManager.fileExists(atPath: destPath!) { 67 | guard let dict = NSMutableDictionary(contentsOfFile: destPath!) else { return .none } 68 | return dict 69 | } else { 70 | return .none 71 | } 72 | } 73 | 74 | func addValuesToPlistFile(_ dictionary:NSDictionary) throws { 75 | let fileManager = FileManager.default 76 | if fileManager.fileExists(atPath: destPath!) { 77 | if !dictionary.write(toFile: destPath!, atomically: false) { 78 | print("[PlistManager] File not written successfully") 79 | throw PlistError.fileNotWritten 80 | } 81 | } else { 82 | throw PlistError.fileDoesNotExist 83 | } 84 | } 85 | 86 | } 87 | 88 | class PlistManager { 89 | static let sharedInstance = PlistManager() 90 | fileprivate init() {} //This prevents others from using the default '()' initializer for this class. 91 | 92 | func startPlistManager() { 93 | if let _ = Plist(name: plistFileName) { 94 | print("[PlistManager] PlistManager started") 95 | } 96 | } 97 | 98 | func addNewItemWithKey(_ key:String, value:AnyObject) { 99 | print("[PlistManager] Starting to add item for key '\(key) with value '\(value)' . . .") 100 | if !keyAlreadyExists(key) { 101 | if let plist = Plist(name: plistFileName) { 102 | 103 | let dict = plist.getMutablePlistFile()! 104 | dict[key] = value 105 | 106 | do { 107 | try plist.addValuesToPlistFile(dict) 108 | } catch { 109 | print(error) 110 | } 111 | print("[PlistManager] An Action has been performed. You can check if it went ok by taking a look at the current content of the plist file: ") 112 | print("[PlistManager] \(plist.getValuesInPlistFile())") 113 | } else { 114 | print("[PlistManager] Unable to get Plist") 115 | } 116 | } else { 117 | print("[PlistManager] Item for key '\(key)' already exists. Not saving Item. Not overwriting value.") 118 | } 119 | 120 | 121 | } 122 | 123 | func removeItemForKey(_ key:String) { 124 | print("[PlistManager] Starting to remove item for key '\(key) . . .") 125 | if keyAlreadyExists(key) { 126 | if let plist = Plist(name: plistFileName) { 127 | 128 | let dict = plist.getMutablePlistFile()! 129 | dict.removeObject(forKey: key) 130 | 131 | do { 132 | try plist.addValuesToPlistFile(dict) 133 | } catch { 134 | print(error) 135 | } 136 | print("[PlistManager] An Action has been performed. You can check if it went ok by taking a look at the current content of the plist file: ") 137 | print("[PlistManager] \(plist.getValuesInPlistFile())") 138 | } else { 139 | print("[PlistManager] Unable to get Plist") 140 | } 141 | } else { 142 | print("[PlistManager] Item for key '\(key)' does not exists. Remove canceled.") 143 | } 144 | 145 | } 146 | 147 | func removeAllItemsFromPlist() { 148 | 149 | if let plist = Plist(name: plistFileName) { 150 | 151 | let dict = plist.getMutablePlistFile()! 152 | 153 | let keys = Array(dict.allKeys) 154 | 155 | if keys.count != 0 { 156 | dict.removeAllObjects() 157 | } else { 158 | print("[PlistManager] Plist is already empty. Removal of all items canceled.") 159 | } 160 | 161 | do { 162 | try plist.addValuesToPlistFile(dict) 163 | } catch { 164 | print(error) 165 | } 166 | print("[PlistManager] An Action has been performed. You can check if it went ok by taking a look at the current content of the plist file: ") 167 | print("[PlistManager] \(plist.getValuesInPlistFile())") 168 | } else { 169 | print("[PlistManager] Unable to get Plist") 170 | } 171 | } 172 | 173 | func saveValue(_ value:AnyObject, forKey:String) { 174 | 175 | if let plist = Plist(name: plistFileName) { 176 | 177 | let dict = plist.getMutablePlistFile()! 178 | 179 | if let dictValue = dict[forKey] { 180 | 181 | if type(of: value) != type(of: dictValue) { 182 | print("[PlistManager] WARNING: You are saving a \(type(of: value)) typed value into a \(type(of: dictValue)) typed value. Best practice is to save Int values to Int fields, String values to String fields etc. (For example: '_NSContiguousString' to '__NSCFString' is ok too; they are both String types) If you believe that this mismatch in the types of the values is ok and will not break your code than disregard this message.") 183 | } 184 | 185 | dict[forKey] = value 186 | 187 | } 188 | 189 | do { 190 | try plist.addValuesToPlistFile(dict) 191 | } catch { 192 | print(error) 193 | } 194 | print("[PlistManager] An Action has been performed. You can check if it went ok by taking a look at the current content of the plist file: ") 195 | print("[PlistManager] \(plist.getValuesInPlistFile())") 196 | } else { 197 | print("[PlistManager] Unable to get Plist") 198 | } 199 | } 200 | 201 | func getValueForKey(_ key:String) -> AnyObject? { 202 | var value:AnyObject? 203 | 204 | 205 | if let plist = Plist(name: plistFileName) { 206 | 207 | let dict = plist.getMutablePlistFile()! 208 | 209 | let keys = Array(dict.allKeys) 210 | //print("[PlistManager] Keys are: \(keys)") 211 | 212 | if keys.count != 0 { 213 | 214 | for (_,element) in keys.enumerated() { 215 | //print("[PlistManager] Key Index - \(index) = \(element)") 216 | if element as! String == key { 217 | print("[PlistManager] Found the Item that we were looking for for key: [\(key)]") 218 | value = dict[key]! as AnyObject? 219 | } else { 220 | //print("[PlistManager] This is Item with key '\(element)' and not the Item that we are looking for with key: \(key)") 221 | } 222 | } 223 | 224 | if value != nil { 225 | //print("[PlistManager] The Element that we were looking for exists: [\(key)]: \(value)") 226 | return value! 227 | } else { 228 | print("[PlistManager] WARNING: The Item for key '\(key)' does not exist! Please, check your spelling.") 229 | return .none 230 | } 231 | 232 | } else { 233 | print("[PlistManager] No Plist Item Found when searching for item with key: \(key). The Plist is Empty!") 234 | return .none 235 | } 236 | 237 | } else { 238 | print("[PlistManager] Unable to get Plist") 239 | return .none 240 | } 241 | 242 | } 243 | 244 | func keyAlreadyExists(_ key:String) -> Bool { 245 | var keyExists = false 246 | 247 | if let plist = Plist(name: plistFileName) { 248 | 249 | let dict = plist.getMutablePlistFile()! 250 | 251 | let keys = Array(dict.allKeys) 252 | //print("[PlistManager] Keys are: \(keys)") 253 | 254 | if keys.count != 0 { 255 | 256 | for (_,element) in keys.enumerated() { 257 | 258 | //print("[PlistManager] Key Index - \(index) = \(element)") 259 | if element as! String == key { 260 | print("[PlistManager] Checked if item exists and found it for key: [\(key)]") 261 | keyExists = true 262 | } else { 263 | //print("[PlistManager] This is Element with key '\(element)' and not the Element that we are looking for with Key: \(key)") 264 | } 265 | } 266 | 267 | } else { 268 | //print("[PlistManager] No Plist Element Found with Key: \(key). The Plist is Empty!") 269 | keyExists = false 270 | } 271 | 272 | } else { 273 | //print("[PlistManager] Unable to get Plist") 274 | keyExists = false 275 | } 276 | 277 | return keyExists 278 | } 279 | 280 | } 281 | 282 | 283 | -------------------------------------------------------------------------------- /Focusin/PomodoroViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PomodoroViewController.swift 3 | // Pomodoro 4 | // 5 | // Created by Alberto Quesada Aranda on 13/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class PomodoroViewController: NSViewController, PreferencesDelegate, NotificationsDelegate { 12 | 13 | @IBOutlet var mainView: PopoverRootView! 14 | var buttonBar: NSStatusBarButton 15 | var popoverView: NSPopover 16 | var tasksView: NSPopover = NSPopover() 17 | 18 | @IBOutlet weak var startButton: NSButton! 19 | @IBOutlet weak var resetButton: NSButton! 20 | @IBOutlet weak var settingsButton: NSButton! 21 | @IBOutlet weak var timeLabel: NSTextField! 22 | @IBOutlet weak var fullPomodoros: NSTextField! 23 | @IBOutlet weak var currentTask: NSTextField! 24 | @IBOutlet weak var removeTaskButton: NSButton! 25 | 26 | @IBOutlet weak var shortBreak: NSButton! 27 | @IBOutlet weak var longBreak: NSButton! 28 | @IBOutlet weak var backgroundButtons: NSTextField! 29 | 30 | let defaults = UserDefaults.standard 31 | var timer: Timer! 32 | var isActive: Bool = false 33 | var isPomodoro: Bool = true 34 | var reloadPreferencesOnNextPomodoro = false 35 | var showTimeInBar: Bool = true 36 | var showNotifications: Bool = true 37 | 38 | var targetPomodoros: Int = 0 39 | var longBreakAfterXPomodoros: Int = 0 40 | 41 | var preferencesWindow: PreferencesWindowController! 42 | var aboutWindow: AboutWindowController! 43 | var notificationsHandler: NotificationsHandler! 44 | 45 | var updateStatusTimer: Foundation.Timer = Foundation.Timer() 46 | var circleAnimations: CircleAnimation! 47 | 48 | let timeFormat = "%d:%02d" 49 | let zeroPomodoros = "0/" 50 | let slash = "/" 51 | let currentTaskLabel = "What are you working on?" 52 | let currentTaskSize: CGFloat = 18 53 | let font = "Lato-Light" 54 | let seconds = 60 55 | 56 | let orange = NSColor.init(red: 0.929, green:0.416, blue:0.353, alpha:1) 57 | let green = NSColor.init(red: 0.608, green:0.757, blue:0.737, alpha:1) 58 | let gray = NSColor.init(red: 0.551, green:0.551, blue:0.551, alpha:1) 59 | 60 | let iconPlayOrange = "play-2" 61 | let iconPauseOrange = "pause-2" 62 | let iconPlayGreen = "play-3" 63 | let iconPauseGreen = "pause-3" 64 | let sofa = "sofa" 65 | let sofaFill = "sofa_filled" 66 | 67 | init(nibName: String, bundle: Bundle?, button: NSStatusBarButton, popover: NSPopover) { 68 | self.buttonBar = button 69 | self.popoverView = popover 70 | super.init(nibName: nibName, bundle: bundle)! 71 | } 72 | 73 | required init?(coder: NSCoder) { 74 | fatalError("init(coder:) has not been implemented") 75 | } 76 | 77 | override func viewDidLoad() { 78 | super.viewDidLoad() 79 | 80 | preferencesWindow = PreferencesWindowController() 81 | aboutWindow = AboutWindowController() 82 | preferencesWindow.delegate = self 83 | 84 | notificationsHandler = NotificationsHandler() 85 | notificationsHandler.delegate = self 86 | 87 | circleAnimations = CircleAnimation(popoverRootView: mainView, startButton: startButton, fullPomodoros: fullPomodoros, shortBreak: shortBreak, longBreak: longBreak) 88 | 89 | timer = Timer(defaults.integer(forKey: Defaults.pomodoroKey), defaults.integer(forKey: Defaults.shortBreakKey), defaults.integer(forKey: Defaults.longBreakKey)) 90 | showTimeInBar = defaults.integer(forKey: Defaults.showTimeKey) == NSOnState 91 | showNotifications = defaults.integer(forKey: Defaults.showNotificationsKey) == NSOnState 92 | targetPomodoros = defaults.integer(forKey: Defaults.targetKey) 93 | longBreakAfterXPomodoros = defaults.integer(forKey: Defaults.longBreakAfterXPomodoros) 94 | resetForPomodoro() 95 | 96 | resetButton.isHidden = true 97 | removeTaskButton.isHidden = true 98 | fullPomodoros.stringValue = zeroPomodoros + defaults.string(forKey: Defaults.targetKey)! 99 | 100 | tasksView.contentViewController = TasksViewController(nibName: "TasksViewController", bundle: nil, popover: popoverView, pomodoroView: self) 101 | tasksView.behavior = NSPopoverBehavior.transient 102 | } 103 | 104 | /* Reset the view elements and get them ready for a new pomodoro */ 105 | func resetForPomodoro() { 106 | let pomodoroDefaultDuration = defaults.integer(forKey: Defaults.pomodoroKey) 107 | timeLabel.stringValue = String(format: timeFormat, pomodoroDefaultDuration/seconds, pomodoroDefaultDuration%seconds) 108 | timeLabel.textColor = orange 109 | circleAnimations.setTimeLayerColor(true) 110 | startButton.image = NSImage(named: iconPlayOrange) 111 | shortBreak.image = NSImage(named: sofa) 112 | reset() 113 | } 114 | 115 | /* Reset the view elements and get them ready for a break */ 116 | func resetForBreak() { 117 | let breakDefaultDuration: Int 118 | if(isLongBreak()) { 119 | breakDefaultDuration = defaults.integer(forKey: Defaults.longBreakKey) 120 | } else { 121 | breakDefaultDuration = defaults.integer(forKey: Defaults.shortBreakKey) 122 | } 123 | timeLabel.stringValue = String(format: timeFormat, breakDefaultDuration/seconds, breakDefaultDuration%seconds) 124 | timeLabel.textColor = green 125 | circleAnimations.setTimeLayerColor(false) 126 | startButton.image = NSImage(named: iconPlayGreen) 127 | shortBreak.image = NSImage(named: sofa) 128 | reset() 129 | } 130 | 131 | /* Common elements of the reset */ 132 | func reset() { 133 | buttonBar.title = showTimeInBar ? timeLabel.stringValue : "" 134 | resetButton.isHidden = true 135 | } 136 | 137 | /* Start a break */ 138 | @IBAction func startBreak(_ sender: AnyObject) { 139 | if(!(!isPomodoro && isActive)) { 140 | isPomodoro = false 141 | resetTimerForBreak() 142 | startTimer() 143 | } 144 | } 145 | 146 | /* Show the todo list of tasks */ 147 | @IBAction func showToDoList(_ sender: AnyObject) { 148 | tasksView.show(relativeTo: longBreak.bounds, of: longBreak, preferredEdge: NSRectEdge.minY) 149 | } 150 | 151 | /* Stop the current timer and reset all the values. */ 152 | @IBAction func resetTimer(_ sender: AnyObject) { 153 | notificationsHandler.removeAllNotifications() 154 | 155 | if(reloadPreferencesOnNextPomodoro) { 156 | reloadPreferences() 157 | } 158 | 159 | if(timer.finishedPomodoros >= targetPomodoros) { 160 | timer.finishedPomodoros = 0 161 | fullPomodoros.stringValue = String(timer.finishedPomodoros) + slash + String(targetPomodoros) 162 | } 163 | 164 | isActive = false 165 | isPomodoro = true 166 | timer.resetTimer(isPomodoro, isLongBreak: isLongBreak()) 167 | updateStatusTimer.invalidate() 168 | resetForPomodoro() 169 | 170 | circleAnimations.resetLayer(Circles.time) 171 | circleAnimations.pauseLayer(Circles.time) 172 | circleAnimations.addTimeLeftAnimation(isPomodoro, isLongBreak: isLongBreak()) 173 | } 174 | 175 | /* Start the timer (if paused or stoped) or pause the timer (if active) */ 176 | @IBAction func startPauseTimer(_ sender: AnyObject) { 177 | notificationsHandler.removeAllNotifications() 178 | 179 | resetButton.isHidden = false 180 | if(isActive) { 181 | isActive = false 182 | timer.pauseTimer() 183 | circleAnimations.pauseLayer(Circles.time) 184 | circleAnimations.pauseLayer(Circles.target) 185 | updateStatusTimer.invalidate() 186 | if(isPomodoro) { 187 | startButton.image = NSImage(named: iconPlayOrange) 188 | } else { 189 | startButton.image = NSImage(named: iconPlayGreen) 190 | } 191 | } else { 192 | if(!updateStatusTimer.isValid) { 193 | updateStatusTimer = Foundation.Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateCurrentStatus), userInfo: nil, repeats: true) 194 | } 195 | if(isPomodoro) { 196 | startButton.image = NSImage(named: iconPauseOrange) 197 | } else { 198 | startButton.image = NSImage(named: iconPauseGreen) 199 | shortBreak.image = NSImage(named: sofaFill) 200 | } 201 | isActive = true 202 | if(timer.unPause(isPomodoro, isLongBreak: isLongBreak())) { 203 | circleAnimations.resumeLayer(Circles.time) 204 | } else { 205 | circleAnimations.restartLayer(Circles.time) 206 | } 207 | if(isPomodoro) { 208 | circleAnimations.resumeLayer(Circles.target) 209 | } 210 | } 211 | } 212 | 213 | /* Check if the break is short or long */ 214 | func isLongBreak() -> Bool { 215 | return timer.finishedPomodoros % longBreakAfterXPomodoros == 0 216 | } 217 | 218 | /* Start a new timer and restart the animation */ 219 | func startTimer() { 220 | startPauseTimer(self) 221 | circleAnimations.addTimeLeftAnimation(isPomodoro, isLongBreak: isLongBreak()) 222 | } 223 | 224 | /* Update the current state of the application. Warn the user when the pomodoro or break finish and ask for the next action*/ 225 | func updateCurrentStatus() { 226 | if(timer.timeLeft >= 0 && timer.timer.isValid) { 227 | timeLabel.stringValue = String(format: timeFormat, timer.timeLeft/seconds, timer.timeLeft%seconds) 228 | if(showTimeInBar) { 229 | buttonBar.title = timeLabel.stringValue 230 | } 231 | } else { 232 | updateStatusTimer.invalidate() 233 | 234 | if(reloadPreferencesOnNextPomodoro) { 235 | reloadPreferences() 236 | } 237 | 238 | circleAnimations.pauseLayer(Circles.target) 239 | 240 | fullPomodoros.stringValue = String(timer.finishedPomodoros) + slash + String(targetPomodoros) 241 | 242 | if(timer.finishedPomodoros == targetPomodoros && isPomodoro) { 243 | isPomodoro = false 244 | notificationsHandler.caller = Caller.target 245 | resetTimerForBreak() 246 | 247 | if(showNotifications) { 248 | notificationsHandler.showNotification("Target achieved!", 249 | text: "Do you want to start the long break?", 250 | actionTitle: "Yes", 251 | otherTitle: "Cancel") 252 | } 253 | } else if(timer.isPomodoro) { 254 | notificationsHandler.caller = Caller.pomodoro 255 | isPomodoro = false 256 | resetTimerForBreak() 257 | 258 | if(showNotifications) { 259 | notificationsHandler.showNotification("Pomodoro completed!", 260 | text: "Do you want to start the break?", 261 | actionTitle: "Ok", 262 | otherTitle: "Cancel") // TODO this is supposed to be a "New Pomodoro" 263 | } 264 | } else { 265 | notificationsHandler.caller = Caller.break 266 | isPomodoro = true 267 | resetTimer(self) 268 | if(showNotifications) { 269 | notificationsHandler.showNotification("Break finished!", 270 | text: "Do you want to start a new pomodoro?", 271 | actionTitle: "New Pomodoro", otherTitle: "Cancel") 272 | } 273 | if(timer.finishedPomodoros >= targetPomodoros) { 274 | timer.finishedPomodoros = 0 275 | } 276 | } 277 | 278 | fullPomodoros.stringValue = String(timer.finishedPomodoros) + slash + String(targetPomodoros) 279 | } 280 | } 281 | 282 | /* Set the timer and the animation ready for start a bread */ 283 | func resetTimerForBreak() { 284 | if(reloadPreferencesOnNextPomodoro) { 285 | reloadPreferences() 286 | } 287 | 288 | timer.resetTimer(isPomodoro, isLongBreak: isLongBreak()) 289 | isActive = false 290 | updateStatusTimer.invalidate() 291 | resetForBreak() 292 | 293 | circleAnimations.resetLayer(Circles.time) 294 | circleAnimations.pauseLayer(Circles.time) 295 | circleAnimations.addTimeLeftAnimation(isPomodoro, isLongBreak: isLongBreak()) 296 | } 297 | 298 | /* Handle the action to perform when the user interact with a notification using the action button */ 299 | func handleNotificationAction(_ caller: Caller) { 300 | if(caller == Caller.target) { 301 | startTimer() 302 | } else if(caller == Caller.pomodoro) { 303 | isPomodoro = false 304 | startTimer() 305 | } else if(caller == Caller.break) { 306 | startTimer() 307 | } 308 | } 309 | 310 | /* Handle the action to perform when the user interact with a notification using the other (close) button */ 311 | func handleNotificationOther(_ caller: Caller) { 312 | if(caller == Caller.pomodoro) { 313 | isPomodoro = true 314 | //resetTimer(self) 315 | startTimer() // start pomodoro 316 | } 317 | } 318 | 319 | /* Handle the notification action: open the popover view of the app */ 320 | func handleNotificationOpenApp() { 321 | NSApp.activate(ignoringOtherApps: true) 322 | popoverView.show(relativeTo: buttonBar.bounds, of: buttonBar, preferredEdge: NSRectEdge.minY) 323 | } 324 | 325 | /* Open a contextual menu with the possible actions: settings, about and quit */ 326 | @IBAction func openSettingsMenu(_ sender: NSButton) { 327 | notificationsHandler.removeAllNotifications() 328 | 329 | let menu = NSMenu() 330 | 331 | menu.insertItem(withTitle: "Reset Full Pomodoros", action: #selector(PomodoroViewController.resetFullPomodoros), 332 | keyEquivalent: "", at: 0) 333 | menu.insertItem(NSMenuItem.separator(), at: 1) 334 | menu.insertItem(withTitle: "Settings", action: #selector(PomodoroViewController.openPreferences), 335 | keyEquivalent: "", at: 2) 336 | menu.insertItem(withTitle: "About", action: #selector(PomodoroViewController.openAbout), 337 | keyEquivalent: "", at: 3) 338 | menu.insertItem(NSMenuItem.separator(), at: 4) 339 | menu.insertItem(withTitle: "Quit", action: #selector(PomodoroViewController.quitApp), 340 | keyEquivalent: "", at: 5) 341 | 342 | // TODO this blocks the timer!! Errorrrrrr 343 | NSMenu.popUpContextMenu(menu, with: NSApplication.shared().currentEvent!, for: sender as NSButton) 344 | } 345 | 346 | /* Set to 0 the current full pomodoros completed */ 347 | func resetFullPomodoros() { 348 | circleAnimations.resetLayer(Circles.target) 349 | timer.finishedPomodoros = 0 350 | fullPomodoros.stringValue = zeroPomodoros + String(targetPomodoros) 351 | } 352 | 353 | /* Open a new window with the preferences of the application */ 354 | func openPreferences() { 355 | preferencesWindow.showWindow(nil) 356 | NSApp.activate(ignoringOtherApps: true) 357 | } 358 | 359 | /* Called when the default preferences are updated */ 360 | func preferencesDidUpdate() { 361 | if(!isActive) { 362 | reloadPreferences() 363 | } else { 364 | self.showTimeInBar = defaults.integer(forKey: Defaults.showTimeKey) == NSOnState 365 | if(!self.showTimeInBar) { 366 | buttonBar.title = "" 367 | } 368 | self.showNotifications = defaults.integer(forKey: Defaults.showNotificationsKey) == NSOnState 369 | reloadPreferencesOnNextPomodoro = true 370 | } 371 | } 372 | 373 | /* Reload the preferred user preferences and override the current settings */ 374 | func reloadPreferences() { 375 | reloadPreferencesOnNextPomodoro = false 376 | timer.pomodoroDuration = defaults.integer(forKey: Defaults.pomodoroKey) 377 | timer.shortBreakDuration = defaults.integer(forKey: Defaults.shortBreakKey) 378 | timer.longBreakDuration = defaults.integer(forKey: Defaults.longBreakKey) 379 | timer.timeLeft = timer.pomodoroDuration 380 | targetPomodoros = defaults.integer(forKey: Defaults.targetKey) 381 | self.showTimeInBar = defaults.integer(forKey: Defaults.showTimeKey) == NSOnState 382 | self.showNotifications = defaults.integer(forKey: Defaults.showNotificationsKey) == NSOnState 383 | resetTimer(self) 384 | } 385 | 386 | /* Open a new window with information about the application */ 387 | func openAbout() { 388 | aboutWindow.showWindow(nil) 389 | NSApp.activate(ignoringOtherApps: true) 390 | } 391 | 392 | /* Close the application */ 393 | func quitApp() { 394 | notificationsHandler.removeAllNotifications() 395 | NSApplication.shared().terminate(self) 396 | } 397 | 398 | /* Save the current text and lose focus on the NSTextFiel */ 399 | @IBAction func enterTask(_ sender: NSTextField) { 400 | notificationsHandler.removeAllNotifications() 401 | 402 | sender.resignFirstResponder() 403 | sender.isSelectable = false 404 | if(!sender.stringValue.isEmpty) { 405 | removeTaskButton.isHidden = false 406 | } else { 407 | currentTask.isEditable = true 408 | } 409 | } 410 | 411 | /* Focus on the NSTextFiel and clear the text */ 412 | @IBAction func removeTask(_ sender: NSButton) { 413 | notificationsHandler.removeAllNotifications() 414 | 415 | currentTask.stringValue = "" 416 | currentTask.isEditable = true 417 | currentTask.becomeFirstResponder() 418 | sender.isHidden = true 419 | } 420 | 421 | } 422 | 423 | 424 | 425 | -------------------------------------------------------------------------------- /Focusin/PomodoroViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 73 | 84 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 115 | 126 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /Focusin/PopoverRootView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopoverRootView.swift 3 | // Pomodoro 4 | // 5 | // Created by Alberto Quesada Aranda on 13/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | /* Override view of the popover to allow change background color */ 12 | class PopoverRootView: NSView { 13 | 14 | override func viewDidMoveToWindow() { 15 | self.wantsLayer = true 16 | if(self.window != nil) { 17 | let aFrameView = self.window?.contentView?.superview 18 | let a = PopoverBackgroundView(frame: aFrameView!.bounds) 19 | a.autoresizingMask = [.viewWidthSizable, .viewHeightSizable] 20 | aFrameView?.addSubview(a, positioned: NSWindowOrderingMode.below, relativeTo: aFrameView) 21 | } 22 | super.viewDidMoveToWindow() 23 | } 24 | 25 | override func draw(_ dirtyRect: NSRect) { 26 | super.draw(dirtyRect) 27 | } 28 | 29 | override func mouseDown(with theEvent: NSEvent) { 30 | super.mouseDown(with: theEvent) 31 | } 32 | 33 | } 34 | 35 | /* Allow to change the background color of the popover view */ 36 | class PopoverBackgroundView: NSView { 37 | 38 | override init(frame: CGRect) { 39 | super.init(frame: frame) 40 | } 41 | 42 | required init?(coder aDecoder: NSCoder) { 43 | super.init(coder: aDecoder) 44 | } 45 | 46 | override func draw(_ rect: CGRect) { 47 | NSColor.init(red: 1, green:1, blue:1, alpha:1).set() 48 | NSRectFill(self.bounds) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Focusin/Preferences/PreferencesWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PreferencesWindowController.swift 3 | // Pomodoro 4 | // 5 | // Created by Alberto Quesada Aranda on 13/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import ServiceManagement 11 | 12 | /* The delegate must implement the methods to take action for the new preferences */ 13 | protocol PreferencesDelegate { 14 | func preferencesDidUpdate() 15 | } 16 | 17 | class PreferencesWindowController: NSWindowController, NSWindowDelegate { 18 | 19 | var delegate: PreferencesDelegate? 20 | 21 | let defaults = UserDefaults.standard 22 | 23 | @IBOutlet weak var pomodoroDuration: NSTextField! 24 | @IBOutlet weak var shortBreakDuration: NSTextField! 25 | @IBOutlet weak var longBreakAfterXPomodoros: NSTextField! 26 | @IBOutlet weak var targetPomodoros: NSTextField! 27 | @IBOutlet weak var showNotifications: NSButton! 28 | @IBOutlet weak var showTimeInBar: NSButton! 29 | @IBOutlet weak var startAtLogin: NSButton! 30 | 31 | @IBOutlet weak var longBreadDuration: NSTextField! 32 | var closedWithButton: Bool = false 33 | let seconds: Int = 60 34 | 35 | let errorTitle = "Invalid value" 36 | let buttonTitle = "Ok" 37 | let errorPomodoro = "Pomodoro duration must be between 1 - 500" 38 | let errorBreak = "Break duration must be between 1 - 500" 39 | let errorTarget = "Target pomodoros must be between 1 - 99" 40 | let errorLongBreak = "Long break must be set between 1 - 99" 41 | 42 | let MIN_TIME = 1 43 | let MAX_TIME = 500 44 | let MIN_TARGET = 1 45 | let MAX_TARGET = 99 46 | 47 | override func windowDidLoad() { 48 | super.windowDidLoad() 49 | self.window?.center() 50 | self.window?.makeKeyAndOrderFront(nil) 51 | NSApp.activate(ignoringOtherApps: true) 52 | 53 | /* Load preferred settings */ 54 | pomodoroDuration.integerValue = defaults.integer(forKey: Defaults.pomodoroKey)/seconds 55 | shortBreakDuration.integerValue = defaults.integer(forKey: Defaults.shortBreakKey)/seconds 56 | longBreadDuration.integerValue = defaults.integer(forKey: Defaults.longBreakKey)/seconds 57 | targetPomodoros.integerValue = defaults.integer(forKey: Defaults.targetKey) 58 | longBreakAfterXPomodoros.integerValue = defaults.integer(forKey: Defaults.longBreakAfterXPomodoros) 59 | showNotifications.state = defaults.integer(forKey: Defaults.showNotificationsKey) 60 | showTimeInBar.integerValue = defaults.integer(forKey: Defaults.showTimeKey) 61 | startAtLogin.integerValue = defaults.integer(forKey: Defaults.startAtLogin) 62 | } 63 | 64 | override var windowNibName : String! { 65 | return "PreferencesWindowController" 66 | } 67 | 68 | /* Save the current settings and close the window */ 69 | @IBAction func savePreferences(_ sender: AnyObject) { 70 | closedWithButton = true 71 | 72 | if(pomodoroDuration.integerValue < MIN_TIME || pomodoroDuration.integerValue > MAX_TIME) { 73 | dialogError(errorPomodoro) 74 | } else if(shortBreakDuration.integerValue < MIN_TIME || shortBreakDuration.integerValue > MAX_TIME) { 75 | dialogError(errorBreak) 76 | } else if(longBreadDuration.integerValue < MIN_TIME || longBreadDuration.integerValue > MAX_TIME) { 77 | dialogError(errorBreak) 78 | } else if(targetPomodoros.integerValue < MIN_TARGET || targetPomodoros.integerValue > MAX_TARGET) { 79 | dialogError(errorTarget) 80 | } else if(longBreakAfterXPomodoros.integerValue < MIN_TARGET || longBreakAfterXPomodoros.integerValue > MAX_TARGET) { 81 | dialogError(errorLongBreak) 82 | } else { 83 | defaults.setValue(pomodoroDuration.integerValue * seconds, forKey: Defaults.pomodoroKey) 84 | defaults.setValue(shortBreakDuration.integerValue * seconds, forKey: Defaults.shortBreakKey) 85 | defaults.setValue(longBreadDuration.integerValue * seconds, forKey: Defaults.longBreakKey) 86 | defaults.setValue(longBreakAfterXPomodoros.integerValue, forKey: Defaults.longBreakAfterXPomodoros) 87 | defaults.setValue(targetPomodoros.integerValue, forKey: Defaults.targetKey) 88 | defaults.setValue(showNotifications.state, forKey: Defaults.showNotificationsKey) 89 | defaults.setValue(showTimeInBar.state, forKey: Defaults.showTimeKey) 90 | defaults.setValue(startAtLogin.state, forKey: Defaults.startAtLogin) 91 | 92 | let launcherAppIdentifier = "com.albertoquesada.LauncherApplication" 93 | SMLoginItemSetEnabled(launcherAppIdentifier as CFString, startAtLogin.state == NSOnState) 94 | 95 | closeAndSave() 96 | self.window?.close() 97 | } 98 | } 99 | 100 | /* Notify of changes to delegates */ 101 | func closeAndSave() { 102 | delegate?.preferencesDidUpdate() 103 | } 104 | 105 | /* Show an alert to the user */ 106 | func dialogError(_ text: String) -> Bool { 107 | let myPopup: NSAlert = NSAlert() 108 | myPopup.messageText = errorTitle 109 | myPopup.informativeText = text 110 | myPopup.alertStyle = NSAlertStyle.warning 111 | myPopup.addButton(withTitle: buttonTitle) 112 | let res = myPopup.runModal() 113 | if res == NSAlertFirstButtonReturn { 114 | return true 115 | } 116 | return false 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /Focusin/Preferences/PreferencesWindowController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 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 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 162 | 170 | 178 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /Focusin/TaskKeys.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TaskKeys.swift 3 | // Focusin 4 | // 5 | // Created by Alberto Quesada Aranda on 17/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /* We only want to store the three more importan task todo */ 12 | struct TaskKeys { 13 | static let task1 = "task1" 14 | static let task1state = "task1state" 15 | static let task2 = "task2" 16 | static let task2state = "task2state" 17 | static let task3 = "task2" 18 | static let task3state = "task3state" 19 | } -------------------------------------------------------------------------------- /Focusin/Tasks/Tasks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | task3 6 | third task 7 | task3state 8 | 9 | task2 10 | second task 11 | task2state 12 | 13 | task1 14 | first task 15 | task1state 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Focusin/Tasks/TasksViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TasksViewController.swift 3 | // Focusin 4 | // 5 | // Created by Alberto Quesada Aranda on 15/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | /* Save only the three most important tasks of the day */ 12 | class TasksViewController: NSViewController { 13 | 14 | @IBOutlet var mainView: PopoverRootView! 15 | 16 | @IBOutlet weak var t1: NSTextField! 17 | @IBOutlet weak var t1s: NSButton! 18 | @IBOutlet weak var t2: NSTextField! 19 | @IBOutlet weak var t2s: NSButton! 20 | @IBOutlet weak var t3: NSTextField! 21 | @IBOutlet weak var t3s: NSButton! 22 | 23 | let pomodoroView: PomodoroViewController 24 | let popoverView: NSPopover 25 | 26 | var task1state: Bool = false 27 | var task2state: Bool = false 28 | var task3state: Bool = false 29 | 30 | init(nibName: String, bundle: Bundle?, popover: NSPopover, pomodoroView: PomodoroViewController) { 31 | self.popoverView = popover 32 | self.pomodoroView = pomodoroView 33 | super.init(nibName: nibName, bundle: bundle)! 34 | PlistManager.sharedInstance.startPlistManager() 35 | } 36 | 37 | required init?(coder: NSCoder) { 38 | fatalError("init(coder:) has not been implemented") 39 | } 40 | 41 | 42 | override func viewDidLoad() { 43 | super.viewDidLoad() 44 | writeDataInView() 45 | } 46 | 47 | /* Write the data saved in the view */ 48 | func writeDataInView() { 49 | t1.stringValue = PlistManager.sharedInstance.getValueForKey(TaskKeys.task1) as! String 50 | t2.stringValue = PlistManager.sharedInstance.getValueForKey(TaskKeys.task2) as! String 51 | t3.stringValue = PlistManager.sharedInstance.getValueForKey(TaskKeys.task3) as! String 52 | 53 | task1state = PlistManager.sharedInstance.getValueForKey(TaskKeys.task1state) as! Bool 54 | task2state = PlistManager.sharedInstance.getValueForKey(TaskKeys.task2state) as! Bool 55 | task3state = PlistManager.sharedInstance.getValueForKey(TaskKeys.task3state) as! Bool 56 | 57 | toggleButton(t1s, status: task1state) 58 | toggleButton(t2s, status: task2state) 59 | toggleButton(t3s, status: task3state) 60 | } 61 | 62 | 63 | @IBAction func task1Set(_ sender: AnyObject) { 64 | PlistManager.sharedInstance.saveValue(t1.stringValue as AnyObject, forKey: TaskKeys.task1) 65 | } 66 | 67 | @IBAction func task2Set(_ sender: AnyObject) { 68 | PlistManager.sharedInstance.saveValue(t2.stringValue as AnyObject, forKey: TaskKeys.task2) 69 | } 70 | 71 | @IBAction func task3Set(_ sender: AnyObject) { 72 | PlistManager.sharedInstance.saveValue(t3.stringValue as AnyObject, forKey: TaskKeys.task3) 73 | } 74 | 75 | @IBAction func task1SetStatus(_ sender: AnyObject) { 76 | task1state = !task1state 77 | toggleButton(t1s, status: task1state) 78 | PlistManager.sharedInstance.saveValue(task1state as AnyObject, forKey: TaskKeys.task1state) 79 | } 80 | 81 | @IBAction func task2SetStatus(_ sender: AnyObject) { 82 | task2state = !task2state 83 | toggleButton(t2s, status: task2state) 84 | PlistManager.sharedInstance.saveValue(task2state as AnyObject, forKey: TaskKeys.task2state) 85 | } 86 | 87 | @IBAction func task3SetStatus(_ sender: AnyObject) { 88 | task3state = !task3state 89 | toggleButton(t3s, status: task3state) 90 | PlistManager.sharedInstance.saveValue(task3state as AnyObject, forKey: TaskKeys.task3state) 91 | } 92 | 93 | /* Toggle the state of a button */ 94 | func toggleButton(_ button: NSButton, status: Bool) { 95 | if(status) { 96 | button.image = NSImage(named: "ok-1") 97 | } else { 98 | button.image = NSImage(named: "ok") 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /Focusin/Tasks/TasksViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /Focusin/Timer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Timer.swift 3 | // Pomodoro 4 | // 5 | // Created by Alberto Quesada Aranda on 13/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class Timer: NSObject { 12 | 13 | let timeInterval = 1.0 14 | var pomodoroDuration: Int = 0 // total duration of a pomodoro 15 | var shortBreakDuration: Int = 0 // total duration of a short break 16 | var longBreakDuration: Int = 0 // total duration of a long break 17 | 18 | var timer: Foundation.Timer = Foundation.Timer() 19 | 20 | var isPomodoro: Bool = true // true if current timer is a pomodoro, false if it is a break 21 | var timeLeft: Int = 0 // time left for the current timer (it can be a pomodoro or a break) 22 | 23 | var finishedPomodoros: Int = 0 // number of pomodoros completed 24 | 25 | /* Init a new timer with a given pomodoro and break duration */ 26 | init(_ pomodoroDuration: Int, _ shortBreakDuration: Int, _ longBreakDuration: Int) { 27 | super.init() 28 | self.pomodoroDuration = pomodoroDuration 29 | self.shortBreakDuration = shortBreakDuration 30 | self.longBreakDuration = longBreakDuration 31 | self.timeLeft = pomodoroDuration 32 | } 33 | 34 | /* Control the time that the current timer has been running and stop when finished */ 35 | func timerControl() { 36 | if(timeLeft > 0) { 37 | timeLeft = timeLeft - 1 38 | } else { 39 | stopTimer() 40 | if(isPomodoro) { 41 | finishedPomodoros += 1 42 | } 43 | } 44 | } 45 | 46 | /* Start the corresponding timer */ 47 | func startTimer() { 48 | self.timer = Foundation.Timer.scheduledTimer(timeInterval: timeInterval, target: self, 49 | selector: #selector(timerControl), userInfo: nil, repeats: true) 50 | } 51 | 52 | /* Start the pomodoro timer */ 53 | func startPomodoroTimer() { 54 | isPomodoro = true 55 | timeLeft = pomodoroDuration 56 | startTimer() 57 | } 58 | 59 | /* Start the short break timer */ 60 | func startShortBreakTimer() { 61 | isPomodoro = false 62 | timeLeft = shortBreakDuration 63 | startTimer() 64 | } 65 | 66 | /* Start the long break timer */ 67 | func startLongBreakTimer() { 68 | isPomodoro = false 69 | timeLeft = longBreakDuration 70 | startTimer() 71 | } 72 | 73 | /* Unpause the currently running timer or if timeLeft is 0, restart it */ 74 | // TODO if pause exactly when the timeLeft is 0 and press play again, the pomodoro starts over... and it should not do that! 75 | func unPause(_ isPomodoro: Bool, isLongBreak: Bool) -> Bool { 76 | if(timeLeft == 0) { 77 | if(isPomodoro) { 78 | startPomodoroTimer() 79 | } else if(isLongBreak) { 80 | startLongBreakTimer() 81 | } else { 82 | startShortBreakTimer() 83 | } 84 | return false 85 | } else { 86 | startTimer() 87 | return true 88 | } 89 | } 90 | 91 | /* Pause the currently running timer */ 92 | func pauseTimer() { 93 | stopTimer() 94 | } 95 | 96 | /* Stop the current timer and reset the pomodoro timer (no break timer) */ 97 | func resetTimer(_ isPomodoro: Bool, isLongBreak: Bool) { 98 | if(self.timer.isValid) { 99 | self.timer.invalidate() 100 | } 101 | if(isPomodoro) { 102 | timeLeft = pomodoroDuration 103 | self.isPomodoro = true 104 | } else if(isLongBreak) { 105 | timeLeft = longBreakDuration 106 | self.isPomodoro = false 107 | } else { 108 | timeLeft = shortBreakDuration 109 | self.isPomodoro = false 110 | } 111 | } 112 | 113 | /* Stop the current timer */ 114 | func stopTimer() { 115 | if(self.timer.isValid) { 116 | self.timer.invalidate() 117 | } 118 | } 119 | 120 | 121 | } 122 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Alberto Quesada 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 | -------------------------------------------------------------------------------- /LauncherApplication/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // LauncherApplication 4 | // 5 | // Created by Alberto Quesada Aranda on 15/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | 15 | 16 | func applicationDidFinishLaunching(aNotification: NSNotification) { 17 | 18 | let mainAppIndentifier = "com.albertoquesada.Focusin" 19 | let running = NSWorkspace.sharedWorkspace().runningApplications 20 | let alreadyRunning = false 21 | 22 | for app in running { 23 | if app.bundleIdentifier == mainAppIndentifier { 24 | alreadyRunning = true 25 | break 26 | } 27 | } 28 | 29 | if !alreadyRunning { 30 | NSDistributedNotificationCenter.defaultCenter().addObserver(self, selector: "terminate", name: "killme", object: mainAppIndentifier) 31 | 32 | let path = NSBundle.mainBundle().bundlePath as NSString 33 | var components = path.pathComponents 34 | 35 | components.removeLast() 36 | components.removeLast() 37 | components.removeLast() 38 | components.append("MacOS") 39 | components.append("Focusin") 40 | 41 | let newPath = NSString.pathWithComponents(components) 42 | NSWorkspace.sharedWorkspace().launchApplication(newPath) 43 | } else { 44 | self.terminate() 45 | } 46 | } 47 | 48 | 49 | func terminate() { 50 | NSApp.terminate(nil) 51 | } 52 | 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /LauncherApplication/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 | } -------------------------------------------------------------------------------- /LauncherApplication/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 Alberto Quesada Aranda. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | LSBackgroundOnly 32 | 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /LauncherApplication/LauncherApplication.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LauncherApplication/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // LauncherApplication 4 | // 5 | // Created by Alberto Quesada Aranda on 15/6/16. 6 | // Copyright © 2016 Alberto Quesada Aranda. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ViewController: NSViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | // Do any additional setup after loading the view. 17 | } 18 | 19 | override var representedObject: AnyObject? { 20 | didSet { 21 | // Update the view, if already loaded. 22 | } 23 | } 24 | 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Pomodoro.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Focusin 2 | 3 | Get stuff done and be more productive by applying the [Pomodoro Technique](http://pomodorotechnique.com). Build in Swift. 4 | 5 | ![Focusin](focusin.png) 6 | 7 | Download it at [http://focusin.albertoquesada.com](http://focusin.albertoquesada.com) 8 | 9 | ## Features 10 | - Native system alerts 11 | - Customizable settings 12 | - Beautiful and minimalistic design 13 | - Menu bar only 14 | - Track your top three tasks. 15 | 16 | ## License 17 | 18 | Focusin is licensed under the MIT license. © [Alberto Quesada](http://albertoquesada.com) 19 | -------------------------------------------------------------------------------- /focusin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoqa/Focusin/3743ad4cfa72f1e954d05dc0147a4d4d6ac9e0f2/focusin.png --------------------------------------------------------------------------------