From ca1e92a1584a4af855028f8a932d626137cb5d0a Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Sat, 22 Feb 2014 09:44:39 -0500 Subject: [PATCH] episode-20-wip --- episode-20-wip/.gitignore | 16 + episode-20-wip/Gemfile | 7 + episode-20-wip/Gemfile.lock | 21 ++ episode-20-wip/Rakefile | 18 + episode-20-wip/app/app_delegate.rb | 53 +++ .../controllers/all_languages_controller.rb | 28 ++ .../controllers/base_languages_controller.rb | 20 ++ .../dynamic_languages_controller.rb | 27 ++ .../app/controllers/idling_window.rb | 83 +++++ .../lock_code/manage_lock_code_controller.rb | 69 ++++ .../lock_code/screen_lock_controller.rb | 72 ++++ .../controllers/more_actions_controller.rb | 121 +++++++ .../static_languages_controller.rb | 26 ++ episode-20-wip/app/models/language.rb | 13 + episode-20-wip/app/models/lock_code.rb | 27 ++ .../app/services/languages_service.rb | 310 ++++++++++++++++++ .../app/stylesheets/application_stylesheet.rb | 220 +++++++++++++ .../app/stylesheets/lock_screen_style.rb | 66 ++++ .../manage_lock_code_stylesheet.rb | 65 ++++ .../app/stylesheets/screen_lock_stylesheet.rb | 21 ++ episode-20-wip/app/views/lock_screen.rb | 103 ++++++ episode-20-wip/resources/Default-568h@2x.png | Bin 0 -> 1659 bytes episode-20-wip/resources/basketball.png | Bin 0 -> 700 bytes episode-20-wip/resources/bell.png | Bin 0 -> 584 bytes episode-20-wip/resources/binocular.png | Bin 0 -> 533 bytes episode-20-wip/resources/lightbulb.png | Bin 0 -> 3023 bytes 26 files changed, 1386 insertions(+) create mode 100644 episode-20-wip/.gitignore create mode 100644 episode-20-wip/Gemfile create mode 100644 episode-20-wip/Gemfile.lock create mode 100644 episode-20-wip/Rakefile create mode 100644 episode-20-wip/app/app_delegate.rb create mode 100644 episode-20-wip/app/controllers/all_languages_controller.rb create mode 100644 episode-20-wip/app/controllers/base_languages_controller.rb create mode 100644 episode-20-wip/app/controllers/dynamic_languages_controller.rb create mode 100644 episode-20-wip/app/controllers/idling_window.rb create mode 100644 episode-20-wip/app/controllers/lock_code/manage_lock_code_controller.rb create mode 100644 episode-20-wip/app/controllers/lock_code/screen_lock_controller.rb create mode 100644 episode-20-wip/app/controllers/more_actions_controller.rb create mode 100644 episode-20-wip/app/controllers/static_languages_controller.rb create mode 100644 episode-20-wip/app/models/language.rb create mode 100644 episode-20-wip/app/models/lock_code.rb create mode 100644 episode-20-wip/app/services/languages_service.rb create mode 100644 episode-20-wip/app/stylesheets/application_stylesheet.rb create mode 100644 episode-20-wip/app/stylesheets/lock_screen_style.rb create mode 100644 episode-20-wip/app/stylesheets/manage_lock_code_stylesheet.rb create mode 100644 episode-20-wip/app/stylesheets/screen_lock_stylesheet.rb create mode 100644 episode-20-wip/app/views/lock_screen.rb create mode 100644 episode-20-wip/resources/Default-568h@2x.png create mode 100755 episode-20-wip/resources/basketball.png create mode 100755 episode-20-wip/resources/bell.png create mode 100755 episode-20-wip/resources/binocular.png create mode 100644 episode-20-wip/resources/lightbulb.png diff --git a/episode-20-wip/.gitignore b/episode-20-wip/.gitignore new file mode 100644 index 0000000..c357662 --- /dev/null +++ b/episode-20-wip/.gitignore @@ -0,0 +1,16 @@ +.repl_history +build +tags +app/pixate_code.rb +resources/*.nib +resources/*.momd +resources/*.storyboardc +.DS_Store +nbproject +.redcar +#*# +*~ +*.sw[po] +.eprj +.sass-cache +.idea diff --git a/episode-20-wip/Gemfile b/episode-20-wip/Gemfile new file mode 100644 index 0000000..acaddc1 --- /dev/null +++ b/episode-20-wip/Gemfile @@ -0,0 +1,7 @@ +source 'https://rubygems.org' + +gem 'rake' +gem 'motion_model' +gem 'ruby_motion_query' +gem 'bubble-wrap' +# Add your dependencies here: diff --git a/episode-20-wip/Gemfile.lock b/episode-20-wip/Gemfile.lock new file mode 100644 index 0000000..5d991e5 --- /dev/null +++ b/episode-20-wip/Gemfile.lock @@ -0,0 +1,21 @@ +GEM + remote: https://rubygems.org/ + specs: + bubble-wrap (1.3.0) + motion-require (0.0.7) + motion-support (0.2.5) + motion-require (>= 0.0.6) + motion_model (0.4.6) + bubble-wrap (= 1.3.0) + motion-support (>= 0.1.0) + rake (10.1.0) + ruby_motion_query (0.5.0) + +PLATFORMS + ruby + +DEPENDENCIES + bubble-wrap + motion_model + rake + ruby_motion_query diff --git a/episode-20-wip/Rakefile b/episode-20-wip/Rakefile new file mode 100644 index 0000000..24b158c --- /dev/null +++ b/episode-20-wip/Rakefile @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +$:.unshift("/Library/RubyMotion/lib") +require 'motion/project/template/ios' + +version = `motion --version`; puts "RubyMotion version: #{version}"; puts '' + +begin + require 'bundler' + Bundler.require +rescue LoadError +end + +require 'bubble-wrap/reactor' + +Motion::Project::App.setup do |app| + # Use `rake config' to see complete project settings. + app.name = 'lrm' +end diff --git a/episode-20-wip/app/app_delegate.rb b/episode-20-wip/app/app_delegate.rb new file mode 100644 index 0000000..4dbfb9b --- /dev/null +++ b/episode-20-wip/app/app_delegate.rb @@ -0,0 +1,53 @@ +class AppDelegate + + attr_reader :language_service + + LOCK_TIME = 10 + + def application(application, didFinishLaunchingWithOptions:launchOptions) + + StandardAppearance.apply + + @window = IdlingWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds) + + setup_lock_screen + + @window.rootViewController = build_tabbar + + @language_service = LanguagesService.new + + @window.makeKeyAndVisible + true + end + + def applicationWillEnterForeground app + @screen_lock_controller.lock_the_screen + end + + private + + def setup_lock_screen + @screen_lock_controller = ScreenLockController.alloc.initWithText("Enter code to unlock") + + App.notification_center.observe(IdlingWindow::BECAME_IDLE, @window) do |_| + @screen_lock_controller.lock_the_screen + end + + @window.set_idle_timeout_seconds LOCK_TIME + end + + def build_tabbar + tabbar = UITabBarController.alloc.init + tabbar.viewControllers = [ + UINavigationController.alloc.initWithRootViewController(StaticLanguagesController.alloc.init), + UINavigationController.alloc.initWithRootViewController(DynamicLanguagesController.alloc.init), + UINavigationController.alloc.initWithRootViewController(AllLanguagesController.alloc.init), + UINavigationController.alloc.initWithRootViewController(MoreActionsController.alloc.init) + ] + tabbar.selectedIndex = 0 + tabbar.delegate = self + tabbar + end + + +end diff --git a/episode-20-wip/app/controllers/all_languages_controller.rb b/episode-20-wip/app/controllers/all_languages_controller.rb new file mode 100644 index 0000000..b744088 --- /dev/null +++ b/episode-20-wip/app/controllers/all_languages_controller.rb @@ -0,0 +1,28 @@ +class AllLanguagesController < BaseLanguagesController + + def init + super + self.tabBarItem = UITabBarItem.alloc.initWithTitle('All', image: rmq.image.resource('bell'), tag: 0) + self.title = 'All' + self + end + + def viewDidLoad + super + + navigationItem.title = 'All languages' + view.dataSource = view.delegate = self + + language_service = rmq.app.delegate.language_service + @data = language_service.all + @keys = @data.map { |r| r.name } + end + + def tableView(tableView, cellForRowAtIndexPath: indexPath) + @reuseIdentifier ||= "CELL_IDENTIFIER_FOR_ALL_LANGUAGES" + super + end + + +end + diff --git a/episode-20-wip/app/controllers/base_languages_controller.rb b/episode-20-wip/app/controllers/base_languages_controller.rb new file mode 100644 index 0000000..3e91f57 --- /dev/null +++ b/episode-20-wip/app/controllers/base_languages_controller.rb @@ -0,0 +1,20 @@ +class BaseLanguagesController < UITableViewController + + def tableView(tableView, numberOfRowsInSection: section) + @data.count + end + + def tableView(tableView, cellForRowAtIndexPath: indexPath) + cell = tableView.dequeueReusableCellWithIdentifier(@reuseIdentifier) + + cell ||= UITableViewCell.alloc.initWithStyle( UITableViewCellStyleValue1, reuseIdentifier:@reuseIdentifier) + + row_number = indexPath.row + key = @keys[row_number] + cell.textLabel.text = key + inventor_name = @data[row_number].inventor_name + cell.detailTextLabel.text = inventor_name + cell + end + +end diff --git a/episode-20-wip/app/controllers/dynamic_languages_controller.rb b/episode-20-wip/app/controllers/dynamic_languages_controller.rb new file mode 100644 index 0000000..c086208 --- /dev/null +++ b/episode-20-wip/app/controllers/dynamic_languages_controller.rb @@ -0,0 +1,27 @@ +class DynamicLanguagesController < BaseLanguagesController + + def init + super + self.tabBarItem = UITabBarItem.alloc.initWithTitle('Dynamic', image: rmq.image.resource('binocular'), tag: 0) + self.title = 'Dynamic' + self + end + + def viewDidLoad + super + + navigationItem.title = 'Dynamic languages' + view.dataSource = view.delegate = self + + language_service = rmq.app.delegate.language_service + @data = language_service.dynamic + @keys = @data.map { |r| r.name } + end + + def tableView(tableView, cellForRowAtIndexPath: indexPath) + @reuseIdentifier ||= "CELL_IDENTIFIER_FOR_DYNAMIC_LANGUAGES" + super + end + +end + diff --git a/episode-20-wip/app/controllers/idling_window.rb b/episode-20-wip/app/controllers/idling_window.rb new file mode 100644 index 0000000..61a50d2 --- /dev/null +++ b/episode-20-wip/app/controllers/idling_window.rb @@ -0,0 +1,83 @@ +# This is how this class works. +# +# A timer is set when application boots and whenver a user event is detected. That timer will invoke method to lock the screen based on LOCK_TIME. +# The only way to cancel that operation is to cancel the timer before the screen is locked. +# +# That timer is cancelled anytime a user event is detected. +# +# So if a user event is not detected within the given LOCK_TIME the screen will be locked. +class IdlingWindow < UIWindow + + BECAME_IDLE = "com.bigbinary.notifications.BecameIdle" + BECAME_ACTIVE = "com.bigbinary.notifications.BecameActive" + + attr_accessor :idle_timer + attr_reader :idle_timeout_seconds + + def initWithFrame(frame) + if super + @idle_timeout_seconds = 0 + end + + self + end + + def sendEvent event + super event + + user_event if user_triggered_event?(event) + end + + def set_idle_timeout_seconds seconds + @idle_timeout_seconds = seconds + + set_timer + end + + def dealloc + if idle_timer + BW::Reactor.cancel_timer(idle_timer) + self.idle_timer = nil + end + + super.dealloc + end + + private + + def user_event + idle_timer ? BW::Reactor.cancel_timer(idle_timer) : became_active_notification + + set_timer + end + + def became_active_notification + App.notification_center.post(BECAME_ACTIVE, self) + end + + def became_idle_notification + App.notification_center.post(BECAME_IDLE, self) + self.idle_timer = nil + end + + def set_timer + BW::Reactor.cancel_timer(idle_timer) if idle_timer + + if (idle_timeout_seconds > 0) + self.idle_timer = BW::Reactor.add_timer(idle_timeout_seconds) do + became_idle_notification + end + end + end + + def user_triggered_event?(event) + all_touches = event.allTouches + if all_touches.count > 0 + phase = all_touches.anyObject.phase + return true if (phase == UITouchPhaseBegan || phase == UITouchPhaseEnded) + end + end + +end + + diff --git a/episode-20-wip/app/controllers/lock_code/manage_lock_code_controller.rb b/episode-20-wip/app/controllers/lock_code/manage_lock_code_controller.rb new file mode 100644 index 0000000..d48aa72 --- /dev/null +++ b/episode-20-wip/app/controllers/lock_code/manage_lock_code_controller.rb @@ -0,0 +1,69 @@ +class ManageLockCodeController < UIViewController + + def initAndSetCode + if init + @text = "Enter a new code" + @action = -> (code) do + controller = ManageLockCodeController.alloc.initAndConfirmCode code + self.navigationController.pushViewController(controller, animated: true) + end + end + + self + end + + def initAndConfirmCode input_code + if init + @text = "Confirm your new code" + end + + @action = -> (confirmation_code) do + stack = self.navigationController.viewControllers + if input_code == confirmation_code + LockCode.set(input_code) + outer_controller = stack.reverse.detect { |controller| controller.class != ManageLockCodeController } + self.navigationController.popToViewController(outer_controller, animated: false) + else + App.alert("Your codes do not match. Please try again.") + controller = stack[stack.size - 2] + self.navigationController.popToViewController(controller, animated: true) + end + end + + self + end + + def initAndChangeCode + if init + @text = "Enter your current code" + @action = -> (code) do + if LockCode.matches?(code) + self.navigationController.pushViewController(ManageLockCodeController.alloc.initAndSetCode, animated: true) + @lock_screen.reset_screen + else + @lock_screen.label.get.text = "Please try again" + @lock_screen.flash_background + @lock_screen.reset_screen + end + end + end + + self + end + + def viewDidAppear(animated) + @lock_screen.reset_screen + end + + def viewDidLoad + super + rmq.stylesheet = ManageLockCodeStylesheet + + @lock_screen = rmq.append(LockScreen, :lock_screen).get + @lock_screen.text = @text + @lock_screen.style_for_controller(self) + @lock_screen.action_on_last_button = @action + end + +end + diff --git a/episode-20-wip/app/controllers/lock_code/screen_lock_controller.rb b/episode-20-wip/app/controllers/lock_code/screen_lock_controller.rb new file mode 100644 index 0000000..112dbe2 --- /dev/null +++ b/episode-20-wip/app/controllers/lock_code/screen_lock_controller.rb @@ -0,0 +1,72 @@ +class ScreenLockController < UIViewController + + DEFAULT_TEXT = "Enter code to unlock" + TRY_AGAIN_TEXT = "Please try again" + + def initWithText(text) + if init + @text = text + end + + self + end + + def viewDidLoad + rmq.stylesheet = ScreenLockStylesheet + + @attempts = 0 + + lock_screen = rmq.append(LockScreen, :lock_screen).get + lock_screen.text = DEFAULT_TEXT + lock_screen.style_for_controller(self) + + lock_screen.action_on_last_button = -> (code) do + (LockCode.enabled? && LockCode.does_not_match?(code)) ? upon_failure(lock_screen) : upon_success(lock_screen) + lock_screen.reset_screen + end + + rmq(lock_screen).append(UIButton, :logout_button).on(:tap) do |sender| + logout + end + end + + def lock_the_screen + return if LockCode.disabled? + + if !@saved_view_controller + @saved_view_controller = App.window.rootViewController + App.window.rootViewController = self + end + end + + private + + def upon_failure lock_screen + lock_screen.label.get.text = TRY_AGAIN_TEXT + lock_screen.flash_background + if @attempts > 1 + logout + @attempts = 0 + else + @attempts += 1 + end + end + + def upon_success lock_screen + @attempts = 0 + unlock_the_screen + lock_screen.label.get.text = DEFAULT_TEXT + end + + def unlock_the_screen + if @saved_view_controller + App.window.rootViewController = @saved_view_controller + @saved_view_controller = nil + end + end + + def logout + App.alert "In a real application you will be logged out" + end + +end diff --git a/episode-20-wip/app/controllers/more_actions_controller.rb b/episode-20-wip/app/controllers/more_actions_controller.rb new file mode 100644 index 0000000..dc72707 --- /dev/null +++ b/episode-20-wip/app/controllers/more_actions_controller.rb @@ -0,0 +1,121 @@ +class MoreActionsController < BaseLanguagesController + + SECURITY_SECTION = 0 + LOCK_CODE_ROW_NUMBER = 0 + CLEAR_CODE_ROW_NUMBER = 1 + LOGOUT_ROW_NUMBER = 2 + NUMBER_OF_ROWS_IN_SECURITY_SECTION = 3 + + APP_VERSION_SECTION = 1 + APP_VERSION_ROW_NUMBER = 0 + NUMBER_OF_ROWS_IN_APP_VERSION_SECTION = 1 + + CLEAR_LOCK_CODE_TEXT = "Clear Lock Code" + SET_LOCK_CODE_TEXT = 'Set Lock Code' + CHANGE_LOCK_CODE_TEXT = 'Change Lock Code' + LOGOUT_TEXT = 'Logout' + + def init + super + initWithStyle(UITableViewStyleGrouped) + self.tabBarItem = UITabBarItem.alloc.initWithTitle('More ..', image: rmq.image.resource('lightbulb'), tag: 0) + self.title = 'More' + self + end + + def viewDidLoad + super + end + + def viewDidAppear animated + view.reloadData + end + + def numberOfSectionsInTableView tableView + 2 + end + + def tableView(tableView, numberOfRowsInSection: indexPath) + if indexPath == 0 + NUMBER_OF_ROWS_IN_SECURITY_SECTION + else + NUMBER_OF_ROWS_IN_APP_VERSION_SECTION + end + end + + CellID = 'CellIdentifier' + def tableView(tableView, cellForRowAtIndexPath: indexPath) + cell = tableView.dequeueReusableCellWithIdentifier(CellID) || UITableViewCell.alloc.initWithStyle(UITableViewCellStyleValue1, reuseIdentifier:CellID) + cell.userInteractionEnabled = true + cell.textLabel.textColor = rmq.color.black + + case indexPath.section + + when SECURITY_SECTION + display_non_version_section indexPath, cell + + when APP_VERSION_SECTION + display_version_section indexPath, cell + end + + cell + end + + def tableView(tableView, titleForHeaderInSection: section) + section == 0 ? "Security" : "App Version Info" + end + + def tableView(tableView, didSelectRowAtIndexPath: indexPath) + selectedCell = tableView.cellForRowAtIndexPath(indexPath) + text = selectedCell.textLabel.text + + case text + when CLEAR_LOCK_CODE_TEXT + handle_clearing_lock_code indexPath + + when SET_LOCK_CODE_TEXT + self.navigationController.pushViewController(ManageLockCodeController.alloc.initAndSetCode, animated: true) + + when CHANGE_LOCK_CODE_TEXT + self.navigationController.pushViewController(ManageLockCodeController.alloc.initAndChangeCode, animated: true) + + when LOGOUT_TEXT + App.alert 'logout functionality is coming up' + + else + App.alert "Error: #{text} was not captured above" + end + end + + private + + def handle_clearing_lock_code indexPath + LockCode.set nil + App.alert 'Lock code has been cleared' + view.reloadData + end + + def display_non_version_section indexPath, cell + case indexPath.row + when LOCK_CODE_ROW_NUMBER + cell.textLabel.text = LockCode.enabled? ? CHANGE_LOCK_CODE_TEXT : SET_LOCK_CODE_TEXT + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator + + when CLEAR_CODE_ROW_NUMBER + cell.textLabel.text = CLEAR_LOCK_CODE_TEXT + + when LOGOUT_ROW_NUMBER + cell.textLabel.text = 'Logout' + end + end + + def display_version_section indexPath, cell + case indexPath.row + when APP_VERSION_ROW_NUMBER + cell.textLabel.text = 'App Version' + cell.userInteractionEnabled = false + cell.detailTextLabel.text = '0.0.1' + end + end + +end diff --git a/episode-20-wip/app/controllers/static_languages_controller.rb b/episode-20-wip/app/controllers/static_languages_controller.rb new file mode 100644 index 0000000..11da737 --- /dev/null +++ b/episode-20-wip/app/controllers/static_languages_controller.rb @@ -0,0 +1,26 @@ +class StaticLanguagesController < BaseLanguagesController + + def init + super + self.tabBarItem = UITabBarItem.alloc.initWithTitle('Static', image: rmq.image.resource('basketball'), tag: 0) + self.title = 'Static' + self + end + + def viewDidLoad + super + + navigationItem.title = 'Static languages' + view.dataSource = view.delegate = self + + language_service = rmq.app.delegate.language_service + @data = language_service.static + @keys = @data.map { |r| r.name } + end + + def tableView(tableView, cellForRowAtIndexPath: indexPath) + @reuseIdentifier ||= "CELL_IDENTIFIER_FOR_STATIC_LANGUAGES" + super + end + +end diff --git a/episode-20-wip/app/models/language.rb b/episode-20-wip/app/models/language.rb new file mode 100644 index 0000000..3c03219 --- /dev/null +++ b/episode-20-wip/app/models/language.rb @@ -0,0 +1,13 @@ +class Language + + include MotionModel::Model + include MotionModel::ArrayModelAdapter + + columns :name => :string, + :static => :boolean, + :dynamic => :boolean, + :wikipedia_link => :string, + :inventor_name => :string, + :inventor_wikipedia_link => :string + +end diff --git a/episode-20-wip/app/models/lock_code.rb b/episode-20-wip/app/models/lock_code.rb new file mode 100644 index 0000000..d237971 --- /dev/null +++ b/episode-20-wip/app/models/lock_code.rb @@ -0,0 +1,27 @@ +class LockCode + + def self.set code + App::Persistence["screen_lock_code"] = code + end + + def self.get + App::Persistence["screen_lock_code"] + end + + def self.enabled? + !!get + end + + def self.disabled? + !self.enabled? + end + + def self.matches? input_code + LockCode.get == input_code + end + + def self.does_not_match? input_code + !matches?(input_code) + end + +end diff --git a/episode-20-wip/app/services/languages_service.rb b/episode-20-wip/app/services/languages_service.rb new file mode 100644 index 0000000..15dcfcc --- /dev/null +++ b/episode-20-wip/app/services/languages_service.rb @@ -0,0 +1,310 @@ +class LanguagesService + + def initialize + build_languages + end + + def all + Language.order(:name).all + end + + def static + Language.order(:name).where(:static).eq(true).all + end + + def dynamic + Language.order(:name).where(:dynamic).eq(true).all + end + + private + + def build_languages + Language.delete_all + + array = %w( + c + java + objective_c + c_plus_plus + php + c_sharp + python + javascript + perl + ruby_lang + pascal + lisp + groovy + cobol + erland + clojure + haskell + ml + scheme + scala + lua + fortran + smalltalk + ocaml + tcl + ) + + languages = array.map { |r| self.send r.intern } + languages.each do |language| + Language.create language + end + end + + def c + { + name: 'C', + static: 'true', + inventor_name: 'Dennis Ritchie', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Dennis_Ritchie' + } + end + + def java + { + name: 'Java', + inventor_name: 'James Gosling', + static: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/James_Gosling' + } + end + + def objective_c + { + name: 'Objective-C', + inventor_name: 'Brad Cox', + static: 'true', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Brad_Cox' + } + end + + def c_plus_plus + { + name: 'C++', + inventor_name: 'Bjarne Stroustrup', + static: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Bjarne_Stroustrup' + } + end + + def php + { + name: 'PHP', + inventor_name: 'Rasmus Lerdorf', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Rasmus_Lerdorf' + } + end + + def c_sharp + { + name: 'C#', + inventor_name: 'Anders Hejlsberg', + static: 'true', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Anders_Hejlsberg' + } + end + + + def python + { + name: 'Python', + inventor_name: 'Guido van Rossum', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Guido_van_Rossum' + } + end + + def javascript + { + name: 'JavaScript', + inventor_name: 'Brendan Eich', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Brendan_Eich' + } + end + + def perl + { + name: 'Perl', + inventor_name: 'Larry Wall', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Larry_Wall' + } + end + + def ruby_lang + { + name: 'Ruby', + inventor_name: 'Matz', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Yukihiro_Matsumoto' + } + end + + def pascal + { + name: 'Pascal', + inventor_name: 'Blaise Pascal', + static: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Blaise_Pascal' + } + end + + def lisp + { + name: 'Lisp', + inventor_name: 'John McCarthy', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist)' + } + end + + def groovy + { + name: 'Groovy', + inventor_name: 'James Strachan', + static: 'true', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/James_Strachan_(programmer)' + } + end + + def cobol + { + name: 'Cobol', + inventor_name: 'Grace Hopper', + static: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Grace_Hopper' + } + end + + def erland + { + name: 'Erlang', + inventor_name: 'Joe Armstrong', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Joe_Armstrong_(programming)' + } + end + + def clojure + { + name: 'Clojure', + inventor_name: 'Rich Hickey', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Clojure' + } + end + + def haskell + { + name: 'Haskell', + inventor_name: 'Simon Peyton Jones', + static: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Simon_Peyton_Jones' + } + end + + def ml + { + name: 'ML', + inventor_name: 'Robin Milner', + static: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Robin_Milner' + } + end + + def scheme + { + name: 'Scheme', + inventor_name: 'Guy L. Steele, Jr.', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Guy_L._Steele' + } + end + + def scala + { + name: 'Scala', + inventor_name: 'Martin Odersky', + static: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Martin_Odersky' + } + end + + def lua + { + name: 'Lua', + inventor_name: 'Roberto Ierusalimschy', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Roberto_Ierusalimschy' + } + end + + def fortran + { + name: 'fortran', + inventor_name: 'John Backus', + static: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/John_Backus'} + end + + def smalltalk + { + name: 'Smalltalk', + inventor_name: 'Alan Kay', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/Alan_Kay' + } + end + + def ocaml + { + name: 'OCaml', + inventor_name: 'INRIA', + static: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/INRIA' + } + end + + def tcl + { + name: 'Tcl', + inventor_name: 'John Ousterhout', + dynamic: 'true', + wikipedia_link: '', + inventor_wikipedia_link: 'http://en.wikipedia.org/wiki/John_Ousterhout' + } + end +end diff --git a/episode-20-wip/app/stylesheets/application_stylesheet.rb b/episode-20-wip/app/stylesheets/application_stylesheet.rb new file mode 100644 index 0000000..44fe433 --- /dev/null +++ b/episode-20-wip/app/stylesheets/application_stylesheet.rb @@ -0,0 +1,220 @@ +class ApplicationStylesheet < RubyMotionQuery::Stylesheet + attr_accessor :margin, :margin_double, :margin_section, :margin_pair, :margin_and_half + + KEYBOARD_HEIGHT = 216 + NAV_HEIGHT = 64 + + def initialize(controller) + super + + @margin_pair = 2 + @margin = 10 + @margin_and_half = 15 + @margin_double = 20 + @margin_section = 40 + end + + def application_setup + end + + def standard_label_title(st) + st.number_of_lines = :unlimited + st.height = 30 + end + + def standard_label_note(st) + st.number_of_lines = :unlimited + st.height = 50 + st.color = color.gray + end + + def standard_button(st) + st.height = 35 + st.color = color.white + st.background_color = color.tint_color + st.view.font = font.large + end + + def standard_button_as_link(st) + st.height = 35 + st.color = color.tint_color + st.view.titleLabel.lineBreakMode = NSLineBreakByWordWrapping + st.view.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft + end + + def standard_text_field(st) + st.height = 25 + st.background_color = color.white + st.view.tap do |view| + #view.text = '' # Keep this here, solves bug + #view.placeholder = 'Foo' + #view.clearButtonMode = UITextFieldViewModeWhileEditing + #view.keyboardType = UIKeyboardTypeDefault + view.font = font.large + view.borderStyle = UITextBorderStyleRoundedRect + #view.autocorrectionType = UITextAutocorrectionTypeYes + #view.autocapitalizationType = UITextAutocapitalizationTypeNone + end + end + + def standard_text_view(st) + st.height = 50 + st.background_color = color.white + st.view.font = font.large + + # Text views don't have UITextBorderStyleRoundedRect, so we'll draw it + st.view.layer.tap do |layer| + layer.borderWidth = 0.5 + layer.borderColor = color.from_hex('B9B9B9').CGColor + layer.cornerRadius = 5 + layer.masksToBounds = true + end + end + + def overlay(st) + st.frame = :full + st.background_color = color.transparent_black + end + def overlay_photo(st) + st.frame = :full + st.view.contentMode = UIViewContentModeScaleAspectFit + end + def overlay_note(st) + st.frame = {l: 0, w: app_width, h: 20} + st.from_bottom = 5 + st.font = font.medium + st.color = color.white + st.text_alignment = :center + st.text = 'Touch anywhere to close' + end + +end + +class StandardAppearance + class << self + def set_named_fonts_and_colors + rmq = RubyMotionQuery::RMQ + font = rmq.font + color = rmq.color + + font_family = 'HelveticaNeue-Light' + font_family_large = 'HelveticaNeue-UltraLight' + font.add_named :huge, font_family_large, 48 + font.add_named :larger, font_family, 30 + font.add_named :large, font_family, 20 + font.add_named :large_bolder, 'HelveticaNeue', 20 + font.add_named :medium, font_family, 16 + font.add_named :small, font_family, 12 + font.add_named :tiny, font_family, 9 + + color.add_named :tint_color, '3F5C7A' + + color.add_named :olive_green, '838E61' + color.add_named :charcoal_gray, '262626' + color.add_named :dim_gray, '7F7F7F' + color.add_named :light_gray, 'F1F1F1' + color.add_named :border_color, 'DDDDDD' + color.add_named :burnt_orange, 'D56217' + color.add_named :light_peach, color.from_rgba(250, 57, 66, 0.330) + + color.add_named :transparent_black, color.from_rgba(0, 0, 0, 0.70) + end + + def apply + # Available items that can be styled in here + # UIActivityIndicatorView + # UIBarButtonItem + # UIBarItem + # UINavigationBar + # UIPopoverController + # UIProgressView + # UISearchBar + # UISegmentedControl + # UISlider + # UISwitch + # UITabBar + # UITabBarItem + # UIToolbar + # UIView + # UIViewController + # UIWindow + + rmq = RubyMotionQuery::RMQ + font = rmq.font + color = rmq.color + + set_named_fonts_and_colors + + UIWindow.appearance.tap do |o| + o.tintColor = color.tint_color + end + + #UIButton.appearance.tap do |o| + #o.tintColor = color.tint_color + #end + + UILabel.appearance.tap do |o| + o.setTextColor color.charcoal_gray + o.setFont font.large + end + + UINavigationBar.appearance.tap do |o| + o.barTintColor = color.olive_green + o.tintColor = color.white + + o.setTitleTextAttributes( { + UITextAttributeFont => font.large, + UITextAttributeTextColor => color.black + }) + end + + UITabBar.appearance.tap do |o| + o.tintColor = color.tint_color + end + UITabBarItem.appearance.tap do |o| + o.setTitleTextAttributes( { + UITextAttributeFont => font.small, + UITextAttributeTextColor => color.dim_gray + }, forState: UIControlStateNormal) + + o.setTitleTextAttributes( { + UITextAttributeFont => font.small, + UITextAttributeTextColor => color.tint_color + }, forState: UIControlStateSelected) + end + + #UIView.appearance.tap do |o| + #o.setTextColor color.charcoal_gray + #o.setFont font.medium + #end + + #UIBarItem.appearance.tap do |o| + #o.setTitleTextAttributes( { + #UITextAttributeFont => font.medium, + #UITextAttributeTextColor => color.white + #}, forState: UIControlStateNormal) + #o.setTitleTextAttributes( { + #UITextAttributeFont => font.large, + #UITextAttributeTextColor => color.white + #}) + #end + + #UIBarButtonItem.appearance.tap do |o| + #o.setTitleTextAttributes( { + #UITextAttributeFont => font.large, + #UITextAttributeTextColor => color.red + #}, forState: UIControlStateNormal) + + #o.setTitleTextAttributes( { + #UITextAttributeFont => font.large, + #UITextAttributeTextColor => color.black + #}, forState: UIControlStateHighlighted) + #end + + + + end + + end +end + diff --git a/episode-20-wip/app/stylesheets/lock_screen_style.rb b/episode-20-wip/app/stylesheets/lock_screen_style.rb new file mode 100644 index 0000000..707c59b --- /dev/null +++ b/episode-20-wip/app/stylesheets/lock_screen_style.rb @@ -0,0 +1,66 @@ +module LockScreenStyle + + SIZE = 60 + BUFFER = SIZE * 0.25 + FULL = SIZE + BUFFER + + def lock_screen(st) + st.frame = :full + st.background_color = color.light_gray + end + + def lock_screen_text(st) + st.font = font.larger + st.top = 100 + st.width = 400 + st.height = 50 + st.centered = :horizontal + st.text_alignment = :center + end + + def lock_screen_digit_block(st) + if device.four_inch? + st.top = 165 + else + st.top = 160 + end + + st.height = SIZE + st.width = BUFFER + FULL * 4 + st.centered = :horizontal + end + + def lock_screen_digit_1(st) + lock_screen_digit(st) + st.left = BUFFER + end + + def lock_screen_digit_2(st) + lock_screen_digit(st) + st.left = BUFFER + FULL + end + + def lock_screen_digit_3(st) + lock_screen_digit(st) + st.left = BUFFER + FULL * 2 + end + + def lock_screen_digit_4(st) + lock_screen_digit(st) + st.left = BUFFER + FULL * 3 + end + + private + + def lock_screen_digit(st) + st.width = SIZE + st.height = SIZE + st.view.font = font.huge + st.view.keyboardType = UIKeyboardTypeDecimalPad + st.view.borderStyle = UITextBorderStyleRoundedRect + st.view.textAlignment = NSTextAlignmentCenter + st.view.secureTextEntry = true + end + +end + diff --git a/episode-20-wip/app/stylesheets/manage_lock_code_stylesheet.rb b/episode-20-wip/app/stylesheets/manage_lock_code_stylesheet.rb new file mode 100644 index 0000000..c0a74f1 --- /dev/null +++ b/episode-20-wip/app/stylesheets/manage_lock_code_stylesheet.rb @@ -0,0 +1,65 @@ +class ManageLockCodeStylesheet < ApplicationStylesheet + + SIZE = 60 + BUFFER = SIZE * 0.25 + FULL = SIZE + BUFFER + + def lock_screen(st) + st.frame = :full + st.background_color = color.light_gray + end + + def lock_screen_text(st) + st.font = font.larger + st.top = 100 + st.width = 400 + st.height = 50 + st.centered = :horizontal + st.text_alignment = :center + end + + def lock_screen_digit_block(st) + if device.four_inch? + st.top = 165 + else + st.top = 160 + end + + st.height = SIZE + st.width = BUFFER + FULL * 4 + st.centered = :horizontal + end + + def lock_screen_digit_1(st) + lock_screen_digit(st) + st.left = BUFFER + end + + def lock_screen_digit_2(st) + lock_screen_digit(st) + st.left = BUFFER + FULL + end + + def lock_screen_digit_3(st) + lock_screen_digit(st) + st.left = BUFFER + FULL * 2 + end + + def lock_screen_digit_4(st) + lock_screen_digit(st) + st.left = BUFFER + FULL * 3 + end + + private + + def lock_screen_digit(st) + st.width = SIZE + st.height = SIZE + st.view.font = font.huge + st.view.keyboardType = UIKeyboardTypeDecimalPad + st.view.borderStyle = UITextBorderStyleRoundedRect + st.view.textAlignment = NSTextAlignmentCenter + st.view.secureTextEntry = true + end + +end diff --git a/episode-20-wip/app/stylesheets/screen_lock_stylesheet.rb b/episode-20-wip/app/stylesheets/screen_lock_stylesheet.rb new file mode 100644 index 0000000..1c4995e --- /dev/null +++ b/episode-20-wip/app/stylesheets/screen_lock_stylesheet.rb @@ -0,0 +1,21 @@ +class ScreenLockStylesheet < ApplicationStylesheet + + include LockScreenStyle + + def logout_button(st) + if device.four_inch? + st.top = 245 + else + st.top = 225 + end + st.width = 120 + st.height = 30 + st.font = font.large_bolder + st.centered = :horizontal + st.view.titleLabel.textAlignment = NSTextAlignmentCenter + st.text = "Logout" + st.color = color.blue + end + +end + diff --git a/episode-20-wip/app/views/lock_screen.rb b/episode-20-wip/app/views/lock_screen.rb new file mode 100644 index 0000000..dc41d2e --- /dev/null +++ b/episode-20-wip/app/views/lock_screen.rb @@ -0,0 +1,103 @@ +class BackspaceDetectingUITextField < UITextField + + def deleteBackward + super + + if @on_backspace + @on_backspace.call(self) + end + end + + def sanitize_data + if text.size > 1 + self.text = text[text.size - 1, 1] + end + end + + def on_backspace(&block) + @on_backspace = block + end +end + +class LockScreen < UIView + + attr_reader :rmq, :digit1, :digit2, :digit3, :digit4, :label, :digits + attr_accessor :text, :action_on_last_button + + def style_for_controller(controller) + @rmq = controller.rmq(self) + + @label = rmq.append(UILabel, :lock_screen_text).each { |l| l.text = @text } + + @digit_block = rmq.append(UIView, :lock_screen_digit_block) + @digit1 = @digit_block.append(BackspaceDetectingUITextField, :lock_screen_digit_1) + @digit2 = @digit_block.append(BackspaceDetectingUITextField, :lock_screen_digit_2) + @digit3 = @digit_block.append(BackspaceDetectingUITextField, :lock_screen_digit_3) + @digit4 = @digit_block.append(BackspaceDetectingUITextField, :lock_screen_digit_4) + + @digits = [@digit1, @digit2, @digit3, @digit4] + + bind_fields_to_events + end + + def reset_screen + digits.each { |d| clear_digit(d) } + bind_fields_to_events + end + + def flash_background + rmq.animate( + duration: 0.1, + animations: -> (rmq) { + rmq.style do |st| + st.background_color = rmq.color.red + end + }, + completion: -> (did_finish, rmq) { + rmq.animate( + duration: 4, + options: UIViewAnimationOptionCurveEaseOut, + animations: -> (rmq) { + rmq.style do |st| + st.background_color = rmq.color.white + end + }) + }) + end + + private + + def bind_fields_to_events + setup_pair(digit1, digit2) + setup_pair(digit2, digit3) + setup_pair(digit3, digit4) + + digit4.on(:change) do |field| + if action_on_last_button + code = @digits.map(&:get).map(&:text).join + action_on_last_button.call code + end + end + + digit1.get.becomeFirstResponder + end + + def setup_pair(left, right) + left.on(:change) do |field| + field.sanitize_data + right.get.becomeFirstResponder unless field.text == "" + end + + right.get.on_backspace do |field| + field.text = "" + left.get.text = "" + left.get.becomeFirstResponder + end + end + + def clear_digit(digit) + digit.off(:change) + digit.get.text = "" + end + +end diff --git a/episode-20-wip/resources/Default-568h@2x.png b/episode-20-wip/resources/Default-568h@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..87de5551f28d702ff56eb369de697e446f20d1f5 GIT binary patch literal 1659 zcmeHGJ#W)M7{0a$p^A!ye^!?p?2?o9{Ju2JSj%#!f?2i-L9yc6seCVmZw6hJj zGEE4Cl)EqskD^q|bZMfi2|ITq2~HsVP?s)}>ex+KV+n<285cZUA+S=CNeQbZ^&V6( z!H6gbZx#sF%9|P{aC#&WE%AF=%cxIdiI*-7IFB`ihQpyeEXpkDBciG*!U|H9f5TzS41%swu62v@U5zH0cE<5JBBr?4yA&lp;Q95mtXc)@e7!eugCCz}g{cmU( z&e17v(aU`QDx5l_n4%U<*>U2D!Sy!sP_b4^sLNU6Fm^QSVspSaO9w25wRTx1(008b z$~_Z?ZEIGPayRm*W$2R7kb}V2aJ^VGcGapuDrG`UrCO|*Mp3C&@LsW6GVzRSFz+~| z5ub7WORl<*n=eBci^v8|f~VB4CoF`MqP1XgE|rCPQ?9=_m(7J-B$7e-X#W`X%q8|G zH|L8h9`n_wk=XY{EcV;yzs|&`xZXT)OmUx|pTGX}>WlbypDkn8$_YP)0 zg!dyU7C?95d#EH_0w92hLjTe1{g3S703<;JWCj1x@Oylc|6xJTfJOCxbbGNH$%GWH zi9q}Sh&RB(86beLp$0V9zb7Vuklg}`tOO|A0ZM~{q#r2?7zibuKpb5JRjfCO;R9e3$nmL(x&FXOH+6bSk9{q#DZw!MW1F--RSB2U}Mnzvu1 z4FZCdS$bvno(d`Z9*h*s2Y4@33>-anHUN9pg>zNpH4(g7@!&`baQ<)>q1wV4>b27<1j%-RK+%_Hrb|X29MY)mJ9s>Yx-VTn%rM>}^KeoboPk zUT%(IdV3$)J`Hxsb07z#Cr6?l&(V>DTv>#WHTy-zqNZgWV0a3WGDlr(4ty|*%zXAJ ihQZm%pSoTC@p=QMG#yzPl4cSB0000PbXFRCwBAV88+XLoov*k^meCKfflRpw#Mwan8;Bnf8y4@e_%48u zCIKv(FoFn|v%eGJs)s;)0bdLe!u`lL|0gkhSOBpCEQT9^xE_cP0P#U6jSdum*dB-z zfcOCr8v+Cn11$h3l{5pf0l^GRAk9mVqA>tV=)NaLqXV%C1Y0gA6lEytnMe!^oY^da z$kc^3ZJ#I7_K!e(f=~fNbin;55yKPCd@?luN4B*eOY+>0gpq*(tk7eGmLBJ!xn==0 zS3SUyfb5}kBQz(H4*&v;k<1*Ijg(aj6~8Cd()(1f6o&5;n{wY% z(Nbie<4o}iG_VjIE6^e#k(A&VkrEPmP#l03+7F@h10vIh0u;+bY4nu+0f+^WY(GZ>-t0e}EvAQtRLG8Y-+wj7C%9{>apb1PGwPgA||%?LQ9Ok4=*U9u5BqrH=JzA+#Q< z;5~AfBLM@D3t5l^<^%BtBtsaHU{JfB+!CM$w=G z2540XdPX25j}DU_KQsM91^-wK@OO7~jX` z(*zujNVW_#`#+KyxEfdck()J4Sh5bG)?y;sav49$83_}0_ XBwhU48vPJd00000NkvXXu0mjff^N=) literal 0 HcmV?d00001 diff --git a/episode-20-wip/resources/lightbulb.png b/episode-20-wip/resources/lightbulb.png new file mode 100644 index 0000000000000000000000000000000000000000..0a111020a48d9ebc81f830d62f3de628bf9dfe03 GIT binary patch literal 3023 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 literal 0 HcmV?d00001