Merge pull request #91 from clearsightstudio/feature/delegate_improvements

PM::Delegate improvements, PushNotification object & methods
This commit is contained in:
Jamon Holmgren
2013-05-30 08:44:54 -07:00
15 changed files with 272 additions and 76 deletions

View File

@@ -1,5 +1,7 @@
class AppDelegate
def on_load(app, options)
open BasicScreen.new(nav_bar: true)
end
end

View File

@@ -6,7 +6,7 @@ require "ProMotion/version"
Motion::Project::App.setup do |app|
original_files = app.files
delegate = File.join(File.dirname(__FILE__), 'ProMotion/delegate.rb')
delegate = File.join(File.dirname(__FILE__), 'ProMotion/delegate/delegate.rb')
promotion_files = FileList[File.join(File.dirname(__FILE__), 'ProMotion/**/*.rb')].exclude(delegate).to_a
app.files = (promotion_files << delegate) + original_files
end

View File

@@ -1,63 +0,0 @@
module ProMotion
class Delegate
include ProMotion::ScreenTabs
include ProMotion::SplitScreen if NSBundle.mainBundle.infoDictionary["UIDeviceFamily"].include?("2") # Only with iPad
attr_accessor :window
def application(application, didFinishLaunchingWithOptions:launch_options)
unless self.respond_to?(:on_load)
PM.logger.error "Your AppDelegate (usually in app_delegate.rb) needs an on_load(application, options) method."
end
on_load(application, launch_options)
true
end
def app_delegate
UIApplication.sharedApplication.delegate
end
def app_window
self.app_delegate.window
end
def home(screen)
screen = screen.new if screen.respond_to?(:new)
@home_screen = screen
end
def load_root_screen(new_screen)
new_screen = new_screen.pm_main_controller
self.window ||= self.ui_window.alloc.initWithFrame(UIScreen.mainScreen.bounds)
self.window.rootViewController = new_screen
self.window.makeKeyAndVisible
end
def ui_window
(defined?(Motion) && defined?(Motion::Xray) && defined?(Motion::Xray::XrayWindow)) ? Motion::Xray::XrayWindow : UIWindow
end
def open_screen(screen, args={})
home(screen)
open_home_screen
end
alias :open :open_screen
alias :open_root_screen :open_screen
def open_home_screen
get_home_screen.send(:on_load) if get_home_screen.respond_to?(:on_load)
load_root_screen get_home_screen
end
def get_home_screen
@home_screen
end
def has_home_screen
@home_screen.nil? == false
end
end
class AppDelegateParent < Delegate; end # For backwards compatibility
end

View File

@@ -0,0 +1,36 @@
module ProMotion
class Delegate
include ProMotion::ScreenTabs
include ProMotion::SplitScreen if NSBundle.mainBundle.infoDictionary["UIDeviceFamily"].include?("2") # Only with iPad
include DelegateHelper
include DelegateNotifications
attr_accessor :window, :aps_notification
def application(application, didFinishLaunchingWithOptions:launch_options)
apply_status_bar
on_load application, launch_options
check_for_notification launch_options
true
end
def applicationWillTerminate(application)
on_unload if respond_to?(:on_unload)
end
end
class AppDelegateParent < Delegate
def self.inherited(klass)
PM.logger.deprecated "PM::AppDelegateParent is deprecated. Use PM::Delegate."
end
end
end

View File

@@ -0,0 +1,68 @@
module ProMotion
module DelegateHelper
def app_delegate
self
end
def app_window
self.window
end
def ui_window
(defined?(Motion) && defined?(Motion::Xray) && defined?(Motion::Xray::XrayWindow)) ? Motion::Xray::XrayWindow : UIWindow
end
def open_screen(screen, args={})
screen = screen.new if screen.respond_to?(:new)
screen.send(:on_load) if screen.respond_to?(:on_load)
@home_screen = screen
self.window ||= self.ui_window.alloc.initWithFrame(UIScreen.mainScreen.bounds)
self.window.rootViewController = screen.pm_main_controller
self.window.makeKeyAndVisible
end
alias :open :open_screen
alias :open_root_screen :open_screen
alias :home :open_screen
def apply_status_bar
self.class.send(:apply_status_bar)
end
def status_bar?
UIApplication.sharedApplication.statusBarHidden
end
module ClassMethods
def status_bar(visible = true, opts={})
@status_bar_visible = visible
@status_bar_opts = opts
end
def apply_status_bar
@status_bar_visible ||= true
@status_bar_opts ||= { animation: :none }
UIApplication.sharedApplication.setStatusBarHidden(!@status_bar_visible, withAnimation:status_bar_animation(@status_bar_opts[:animation]))
end
def status_bar_animation(opt)
{
fade: UIStatusBarAnimationFade,
slide: UIStatusBarAnimationSlide,
none: UIStatusBarAnimationNone
}[opt] || UIStatusBarAnimationNone
end
end
def self.included(base)
base.extend(ClassMethods)
end
end
end

View File

@@ -0,0 +1,61 @@
module ProMotion
module DelegateNotifications
attr_accessor :aps_notification
def check_for_notification(options)
if options && options[UIApplicationLaunchOptionsRemoteNotificationKey]
received_notification options[UIApplicationLaunchOptionsRemoteNotificationKey]
end
end
def register_for_notifications(*notification_types)
notification_types = Array.new(notification_types)
notification_types = [ :badge, :sound, :alert, :newsstand ] if notification_types.include?(:all)
types = UIRemoteNotificationTypeNone
types = types | UIRemoteNotificationTypeBadge if notification_types.include?(:badge)
types = types | UIRemoteNotificationTypeSound if notification_types.include?(:sound)
types = types | UIRemoteNotificationTypeAlert if notification_types.include?(:alert)
types = types | UIRemoteNotificationTypeNewsstandContentAvailability if notification_types.include?(:newsstand)
UIApplication.sharedApplication.registerForRemoteNotificationTypes types
end
def unregister_for_notifications
UIApplication.sharedApplication.unregisterForRemoteNotifications
end
def registered_notifications
mask = UIApplication.sharedApplication.enabledRemoteNotificationTypes
types = []
types << :badge if mask & UIRemoteNotificationTypeBadge
types << :sound if mask & UIRemoteNotificationTypeSound
types << :alert if mask & UIRemoteNotificationTypeAlert
types << :newsstand if mask & UIRemoteNotificationTypeNewsstandContentAvailability
types
end
def received_notification(notification)
@aps_notification = PM::PushNotification.new(notification)
on_notification(@aps_notification) if respond_to?(:on_notification)
end
# CocoaTouch
def application(application, didRegisterForRemoteNotificationsWithDeviceToken:device_token)
on_registration(device_token, nil) if respond_to?(:on_registration)
end
def application(application, didFailToRegisterForRemoteNotificationsWithError:error)
on_registration(nil, error) if respond_to?(:on_registration)
end
def application(application, didReceiveRemoteNotification:notification)
received_notification(notification)
end
end
end

View File

@@ -50,7 +50,7 @@ module ProMotion
end
def frame_from_array(array)
PM.logger.deprecated "`frame_from_array` is deprecated and will be removed. Use RubyMotion's built-in [[x, y], [width, height]]."
PM.logger.deprecated "`frame_from_array` is deprecated and will be removed. Use RubyMotion's built-in [[x, y], [width, height]] or CGRectMake(x, y, w, h)."
return CGRectMake(array[0], array[1], array[2], array[3]) if array.length == 4
PM.logger.error "frame_from_array expects an array with four elements: [x, y, width, height]"
CGRectZero.dup

View File

@@ -0,0 +1,51 @@
module ProMotion
class PushNotification
attr_accessor :notification
def initialize(n)
self.notification = n
end
def to_s
self.notification.inspect
end
def to_json
PM.logger.warn "PM::PushNotification.to_json not implemented yet."
end
def aps
self.notification["aps"]
end
def alert
aps["alert"] if aps
end
def badge
aps["badge"] if aps
end
def sound
aps["sound"] if aps
end
# For testing from the REPL
# > PM::PushNotification.simulate alert: "My test message", badge: 4
def self.simulate(args = {})
UIApplication.sharedApplication.delegate.on_notification self.fake_notification(args)
end
def self.fake_notification(args = {})
self.new({
"aps" => {
"alert" => args[:alert] || "Test Push Notification",
"badge" => args[:badge] || 2,
"sound" => args[:sound] || "default"
}
})
end
end
end

View File

@@ -52,7 +52,7 @@ module ProMotion
args ||= {}
args[:animated] ||= true
if self.is_modal?
if self.modal?
close_modal_screen args
elsif self.navigation_controller

View File

@@ -34,9 +34,9 @@ module ProMotion
def open_tab_bar(*screens)
tab_bar = tab_bar_controller(*screens)
a = self.respond_to?(:load_root_screen) ? self : UIApplication.sharedApplication.delegate
a = self.respond_to?(:open_root_screen) ? self : UIApplication.sharedApplication.delegate
a.load_root_screen(tab_bar)
a.open_root_screen(tab_bar)
tab_bar
end

View File

@@ -27,13 +27,23 @@ module ProMotion
end
def is_modal?
PM.logger.deprecated "`is_modal?` is deprecated. Use `modal?`."
modal?
end
def modal?
self.modal == true
end
def has_nav_bar?
self.navigation_controller.nil? != true
PM.logger.deprecated "`has_nav_bar? is deprecated. Use `nav_bar?`."
nav_bar?
end
def nav_bar?
!!self.navigation_controller
end
def navigation_controller
@navigation_controller ||= self.navigationController
end
@@ -45,7 +55,7 @@ module ProMotion
# [DEPRECATED]
def load_view_controller
warn "[DEPRECATION] `load_view_controller` is deprecated and doesn't actually do anything anymore. You can safely remove it from your code."
PM.logger.deprecated "`load_view_controller` is deprecated and doesn't actually do anything anymore. You can safely remove it from your code."
end
def set_tab_bar_item(args = {})

View File

@@ -1,4 +1,6 @@
class TestDelegate < ProMotion::Delegate
status_bar false
def on_load(app, options)
end
end

View File

@@ -0,0 +1,29 @@
describe "PM::Delegate" do
before { @subject = TestDelegate.new }
it 'should call on_load on launch' do
@subject.mock!(:on_load) do |app, options|
options[:jamon].should.be.true
app.should.be.kind_of(UIApplication)
end
@subject.application(UIApplication.sharedApplication, didFinishLaunchingWithOptions:{jamon: true})
end
it "should handle push notifications" do
@subject.mock!(:on_notification) do |notification|
notification.should.be.kind_of(PM::PushNotification)
notification.alert.should == "Eating Bacon"
notification.badge.should == 42
notification.sound.should == "jamon"
@subject.aps_notification.should == notification
end
launch_options = { UIApplicationLaunchOptionsRemoteNotificationKey => PM::PushNotification.fake_notification(alert: "Eating Bacon", badge: 42, sound: "jamon").notification }
@subject.application(nil, didFinishLaunchingWithOptions:launch_options )
end
end

View File

@@ -116,9 +116,9 @@ describe "screen helpers" do
new_screen.parent_screen.should == @screen
new_screen.title.should == 'Some Title'
new_screen.is_modal?.should == true
new_screen.modal?.should == true
new_screen.hidesBottomBarWhenPushed.should == true
new_screen.has_nav_bar?.should == true
new_screen.nav_bar?.should == true
end
it "should present the #main_controller when showing a modal screen" do

View File

@@ -31,8 +31,8 @@ describe "screen properties" do
HomeScreen.debug_mode.should == true
end
it "#is_modal? should be true" do
@screen.is_modal?.should == true
it "#modal? should be true" do
@screen.modal?.should == true
end
it "should know it is the first screen" do
@@ -115,7 +115,7 @@ describe "screen properties" do
describe "navigation controller behavior" do
it "should have a nav bar" do
@screen.has_nav_bar?.should == true
@screen.nav_bar?.should == true
end
it "#main_controller should return a navigation controller" do