bluepotion 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -0
  3. data/lib/project/alert_dialog/alert_dialog.rb +26 -4
  4. data/lib/project/benchmark.rb +34 -0
  5. data/lib/project/delayed_execution.rb +16 -0
  6. data/lib/project/ext/object.rb +16 -15
  7. data/lib/project/ext/time.rb +42 -0
  8. data/lib/project/ext/view.rb +26 -5
  9. data/lib/project/potion.rb +9 -0
  10. data/lib/project/pro_motion/activities/pm_activity.rb +15 -0
  11. data/lib/project/pro_motion/activities/pm_navigation_activity.rb +8 -1
  12. data/lib/project/pro_motion/activities/pm_single_fragment_activity.rb +1 -1
  13. data/lib/project/pro_motion/adapters/pm_base_adapter.rb +79 -15
  14. data/lib/project/pro_motion/adapters/pm_cursor_adapter.rb +7 -31
  15. data/lib/project/pro_motion/fragments/pm_list_screen.rb +14 -11
  16. data/lib/project/pro_motion/fragments/pm_screen.rb +3 -0
  17. data/lib/project/pro_motion/fragments/pm_screen_module.rb +41 -2
  18. data/lib/project/pro_motion/pm_application.rb +63 -0
  19. data/lib/project/ruby_motion_query/rmq/actions.rb +10 -0
  20. data/lib/project/ruby_motion_query/rmq/base.rb +79 -25
  21. data/lib/project/ruby_motion_query/rmq/config.rb +9 -0
  22. data/lib/project/ruby_motion_query/rmq/event_wrappers/rmq_keyboard_action.rb +12 -0
  23. data/lib/project/ruby_motion_query/rmq/event_wrappers/rmq_number_picker_change.rb +12 -0
  24. data/lib/project/ruby_motion_query/rmq/events.rb +14 -3
  25. data/lib/project/ruby_motion_query/rmq/factory.rb +10 -5
  26. data/lib/project/ruby_motion_query/rmq/selectors.rb +7 -6
  27. data/lib/project/ruby_motion_query/rmq/styles.rb +33 -10
  28. data/lib/project/ruby_motion_query/rmq/subviews.rb +2 -3
  29. data/lib/project/ruby_motion_query/rmq/traverse.rb +44 -6
  30. data/lib/project/ruby_motion_query/rmq_color.rb +2 -1
  31. data/lib/project/ruby_motion_query/rmq_device.rb +6 -0
  32. data/lib/project/ruby_motion_query/rmq_resource.rb +8 -0
  33. data/lib/project/ruby_motion_query/rmq_screen_data.rb +5 -0
  34. data/lib/project/ruby_motion_query/rmq_stylesheet.rb +30 -4
  35. data/lib/project/ruby_motion_query/rmq_validation.rb +3 -3
  36. data/lib/project/ruby_motion_query/rmq_view_data.rb +33 -1
  37. data/lib/project/ruby_motion_query/stylers/rmq_text_view_styler.rb +11 -3
  38. data/lib/project/ruby_motion_query/stylers/rmq_view_styler.rb +37 -3
  39. data/lib/project/version.rb +1 -1
  40. metadata +10 -4
  41. data/lib/project/compatibility/motion_keychain.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fdb0d8c6161c235baac6480838e5e1cabe94c6fa
4
- data.tar.gz: b908f87be8c23fd810d18a48052bca355ad6c731
3
+ metadata.gz: 621ef614c7cdeca5215428c03e6b965d5562be5a
4
+ data.tar.gz: dacd0d747992dcfbb1955c12b72eb0330619feee
5
5
  SHA512:
6
- metadata.gz: caf24b6c2f5646512dff78031b1e02e3ff7149013f15da9ec9daaee3aa51329f45ef14c5eaed5a26a12c587732e90e38a2e82e673e36155f986eee5bbce865da
7
- data.tar.gz: def61d81138b2120c82ac3745a59e96879d7076b2f9b8810e4a0c188b31060ac8fb992f2f5f07789f3c11f63631d5f692ecbda79aaf7e1d14f4f8d0417bd6eeb
6
+ metadata.gz: f38534a7f09220f82b5a8a7cf623f8536280df078b9cfd472980c7902aa8ae897b9fbbe85ca55d44af80db3d8eca055804d2bd02efc8e9f4e2f2104cfc9da381
7
+ data.tar.gz: 51d14f5d4f7c13130c76ce108b423d33bb2023284ae4964c8e99b6b82cdc521fbf1b600e2db699b0cf125f0fe58868e2f372d8215066e9cdfc62cd6059976fea
data/README.md CHANGED
@@ -5,6 +5,13 @@
5
5
  BluePotion
6
6
  -----------
7
7
 
8
+ ## Prerequisites
9
+
10
+ - RubyMotion android: `motion android-setup` (install API 16)
11
+ - Gradle: `brew install gradle`
12
+ - GenyMotion Emulator (optional) [Gant Laborde's post on Genymotion](http://www.iconoclastlabs.com/blog/rubymotion-android-in-the-emulator-with-genymotion).
13
+ - For running on your device, [do this](http://www.kingoapp.com/root-tutorials/how-to-enable-usb-debugging-mode-on-android.htm)
14
+
8
15
  ## Warning, BluePotion is an alpha release.
9
16
 
10
17
  BluePotion is the Android version of [RedPotion](http://redpotion.org). We're spending a lot of time working on it right now. It's currently in Alpha.
@@ -9,6 +9,11 @@
9
9
  # mp "Fine!"
10
10
  # end
11
11
  # end
12
+ #
13
+ # Example of alert with input
14
+ # app.alert(title: "What's your name?", style: :input) do |choice, input_text|
15
+ # mp "User clicked #{choice} and typed #{input_text}"
16
+ # end
12
17
 
13
18
  # Generic AlertDialog
14
19
  class AlertDialog < Android::App::DialogFragment
@@ -19,11 +24,12 @@ class AlertDialog < Android::App::DialogFragment
19
24
  @options = {
20
25
  theme: Android::App::AlertDialog::THEME_HOLO_LIGHT,
21
26
  title: "Alert!",
22
- message: "",
27
+ message: nil,
23
28
  positive_button: "OK",
24
29
  negative_button: "Cancel",
25
30
  positive_button_handler: self,
26
31
  negative_button_handler: self,
32
+ style: nil,
27
33
  show: true
28
34
  }.merge(options)
29
35
 
@@ -38,7 +44,7 @@ class AlertDialog < Android::App::DialogFragment
38
44
  end
39
45
 
40
46
  def onCreateDialog(saved_instance_state)
41
- builder = Android::App::AlertDialog::Builder.new(getActivity(), @options[:theme])
47
+ builder = Android::App::AlertDialog::Builder.new(activity, @options[:theme])
42
48
 
43
49
  builder.title = @options[:title]
44
50
  builder.message = @options[:message]
@@ -48,15 +54,31 @@ class AlertDialog < Android::App::DialogFragment
48
54
  builder.setNegativeButton(@options[:negative_button], @options[:negative_button_handler]) if @options[:negative_button]
49
55
 
50
56
  # Add custom view?
57
+ @options[:view] = simple_text_view if @options[:style] == :input
51
58
  builder.view = @options[:view] if @options[:view]
52
59
 
53
60
  # DONE!
54
- builder.create()
61
+ builder.create
62
+ end
63
+
64
+ def simple_text_view
65
+ # Set up the input
66
+ input = Potion::EditText.new(activity)
67
+ input.singleLine = true
68
+ input.id = @text_view_id = Potion::ViewIdGenerator.generate
69
+ # possible input types - future feature
70
+ #input.inputType = (Android::Text::InputType.TYPE_CLASS_TEXT | Android::Text::InputType.TYPE_TEXT_VARIATION_PASSWORD)
71
+ input
55
72
  end
56
73
 
57
74
  def onClick(dialog, id)
58
75
  button_text = (id == Android::App::AlertDialog::BUTTON_POSITIVE) ? @options[:positive_button] : @options[:negative_button]
59
- @callback.call(button_text) if @callback
76
+
77
+ # if a text_view is present, grab what the user gave us
78
+ text_view = @text_view_id && dialog.findViewById(@text_view_id)
79
+ input_text = text_view ? text_view.text.toString : nil
80
+
81
+ @callback.call(button_text, input_text) if @callback
60
82
  end
61
83
 
62
84
  end
@@ -0,0 +1,34 @@
1
+ class Benchmark
2
+ class << self
3
+ def decimal_formatter
4
+ @_decimal_formatter ||= Potion::DecimalFormat.new("#,###,###")
5
+ end
6
+
7
+ def total_run
8
+ @total_run
9
+ end
10
+
11
+ def run_single(setup_desc, code_desc, iterations, &block)
12
+ @total_run ||= 0
13
+ start_time = Time.milliseconds_since_epoch
14
+ iterations.times do
15
+ @total_run += 1
16
+ block.call
17
+ #mp "[#{@total_run}]"
18
+ end
19
+ end_time = Time.milliseconds_since_epoch
20
+
21
+ total_time = end_time - start_time
22
+ per_item = total_time / iterations.to_f
23
+ out = "\nBenchmark Started at: #{decimal_formatter.format(start_time)}"
24
+ out << "\n Iterations: #{decimal_formatter.format(iterations)}"
25
+ out << "\n Setup: #{setup_desc}\n Code: #{code_desc}"
26
+ out << "\n Total milliseconds: #{decimal_formatter.format(total_time)}"
27
+ out << "\n Milliseconds per iteration: #{per_item}"
28
+ out << "\n RMQ allocations: #{$rmq_initialized.to_s}"
29
+ mp out
30
+ Potion::System.gc
31
+ out
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ module DelayedExecution
2
+
3
+ def self.after(delay, &block)
4
+ Potion::Handler.new.postDelayed(BlockRunnable.new(&block), delay * 1000.0)
5
+ end
6
+
7
+ class BlockRunnable
8
+ def initialize(&block)
9
+ @block = block
10
+ end
11
+
12
+ def run
13
+ @block.call
14
+ end
15
+ end
16
+ end
@@ -31,7 +31,7 @@ class Object
31
31
  out << klass_name.to_s
32
32
  end
33
33
 
34
- out
34
+ mp out
35
35
  end
36
36
 
37
37
  def inspect
@@ -68,7 +68,6 @@ class Object
68
68
  rmq(*args).get
69
69
  end
70
70
 
71
-
72
71
  # BluePotion stuff
73
72
 
74
73
  # REMOVE when mp starts working
@@ -77,25 +76,28 @@ class Object
77
76
  return unless RMQ.debugging?
78
77
  end
79
78
 
79
+ @@mp_backspace ||= "\b\b " * (Android::App::Application.name.length + 20)
80
+
80
81
  if s.nil?
81
82
  s = "<nil>"
82
- elsif s.is_a?(Array)
83
- s = s.map{|e| e.inspect }.join("\n")
83
+ #elsif s.is_a?(Array) # TODO - THIS DOESN'T WORK
84
+ #s = s.map{|e| e.inspect }.join("\n")
84
85
  else
85
86
  s = s.to_s
86
87
  end
87
- backspace = "\b\b " * (Android::App::Application.name.length + 20)
88
+
88
89
  lines = s.split("\n")
89
- lines.each do |line|
90
- if RMQ.debugging?
91
- out = backspace
92
- out << "\e[1;#{36}m#{self.object_id}\e[0m #{self.short_class_name}".ljust(50)
93
- out << " \e[1;#{34}m#{line}\e[0m"
94
- puts out
95
- else
96
- puts "#{backspace} \e[1;#{34}m#{line}\e[0m"
97
- end
90
+ @@mp_tproc ||= proc do |line| # This hack fixes RMA bug, TODO remove when RMA block retention bug is fixed
91
+ #if RMQ.debugging?
92
+ #out = @@mp_backspace
93
+ #out << "\e[1;#{36}m#{self.object_id}\e[0m #{self.short_class_name}".ljust(50)
94
+ #out << " \e[1;#{34}m#{line}\e[0m"
95
+ #puts out
96
+ #else
97
+ puts "#{@@mp_backspace} \e[1;#{34}m#{line}\e[0m"
98
+ #end
98
99
  end
100
+ lines.each &@@mp_tproc
99
101
  end
100
102
 
101
103
  def app
@@ -106,5 +108,4 @@ class Object
106
108
  rmq.device
107
109
  end
108
110
 
109
-
110
111
  end
@@ -0,0 +1,42 @@
1
+ class Time
2
+ def self.iso8601(iso8601_string)
3
+ timestring = iso8601_string.toString
4
+ timestring = timestring.replaceAll("Z", "+00:00")
5
+ timestring = timestring.substring(0, 22) + timestring.substring(23)
6
+ date = Java::Text::SimpleDateFormat.new("yyyy-MM-dd'T'HH:mm:ss+SSS").parse(timestring)
7
+ Time.new(date.getTime())
8
+ end
9
+
10
+ def self.milliseconds_since_epoch
11
+ Java::Lang::System.currentTimeMillis.doubleValue
12
+ end
13
+
14
+ def today?
15
+ today = Time.now
16
+ (self.year == today.year) && (self.month == today.month) && (self.date == today.date)
17
+ end
18
+
19
+ def strftime(str)
20
+ converter = {
21
+ #Ruby => Android
22
+ '%A' => 'EEEE',
23
+ '%b' => 'MMM',
24
+ '%-e' => 'd',
25
+ '%-l' => 'h',
26
+ '%P' => 'a',
27
+ '%M' => 'mm',
28
+ '%P' => 'a',
29
+ '%m' => 'MM',
30
+ '%d' => 'dd',
31
+ '%Y' => 'yyyy'
32
+ }
33
+
34
+ converted = str.toString
35
+ converter.each do |k,v|
36
+ converted = converted.replaceAll(k, v)
37
+ end
38
+
39
+ formatter = Java::Text::SimpleDateFormat.new(converted)
40
+ formatter.format(self).to_s
41
+ end
42
+ end
@@ -3,6 +3,18 @@ class Android::View::View
3
3
  "<#{id} #{short_class_name}>"
4
4
  end
5
5
 
6
+ #def onDestroy
7
+ #mp 'onDestroy view'
8
+ #super
9
+ #end
10
+
11
+ def cleanup
12
+ if @_rmq_data
13
+ @_rmq_data.cleanup
14
+ @_rmq_data = nil
15
+ end
16
+ end
17
+
6
18
  def to_s
7
19
  self.inspect
8
20
  end
@@ -37,13 +49,21 @@ class Android::View::View
37
49
  def on_styled
38
50
  end
39
51
 
40
- def rmq(*working_selectors)
41
- q = RMQ.create_with_selectors(working_selectors, self) #.tap do |o|
42
- q
43
- #if vc = self.rmq_data.view_controller
44
- #o.weak_view_controller = vc
52
+ #def rmq(*working_selectors)
53
+ #crmq = (rmq_data.cached_rmq ||= RMQ.create_with_selectors([], self))
54
+
55
+ #if working_selectors.length == 0
56
+ #crmq
57
+ #else
58
+ #RMQ.create_with_selectors(working_selectors, self, crmq)
45
59
  #end
46
60
  #end
61
+
62
+ def find(*working_selectors) # Not calling rmq below for performance reasons (one less method invocation)
63
+ RMQ.create_with_selectors(working_selectors, self)
64
+ end
65
+ def rmq(*working_selectors)
66
+ RMQ.create_with_selectors(working_selectors, self)
47
67
  end
48
68
 
49
69
  def color(*params)
@@ -67,6 +87,7 @@ class Android::View::View
67
87
  end
68
88
 
69
89
  def subviews
90
+ # TODO, see if anyone uses this, and remove
70
91
  out = []
71
92
 
72
93
  if self.is_a?(Potion::ViewGroup)
@@ -14,6 +14,8 @@ module Potion
14
14
  BaseAdapter = Android::Widget::BaseAdapter
15
15
  Dialog = Android::App::Dialog
16
16
  EditorInfo = Android::View::Inputmethod::EditorInfo
17
+ System = Java::Lang::System
18
+ DecimalFormat = Java::Text::DecimalFormat
17
19
 
18
20
  # Layouts
19
21
  LayoutInflater = Android::View::LayoutInflater
@@ -35,12 +37,19 @@ module Potion
35
37
  # Graphics
36
38
  Color = Android::Graphics::Color
37
39
  Typeface = Android::Graphics::Typeface
40
+ Bitmap = Android::Graphics::Bitmap
38
41
 
39
42
  # Media
40
43
  File = Java::Io::File
41
44
  FileOutputStream = Java::Io::FileOutputStream
42
45
  MediaStore = Android::Provider::MediaStore
43
46
  Contacts = Android::Provider::ContactsContract::Contacts
47
+ # This is needed since you can't access constants of interfaces
48
+ # Basically is Android::Provider::MediaStore::Images::Media::INTERNAL_CONTENT_URI
49
+ INTERNAL_CONTENT_URI = Potion::Uri.parse("content://media/internal/images/media")
50
+ PHONE_CONTENT_URI = Potion::Uri.parse("content://com.android.contacts/data/phones")
51
+
52
+ Handler = Android::Os::Handler
44
53
  end
45
54
 
46
55
  #
@@ -27,6 +27,11 @@
27
27
  # Abstract
28
28
  end
29
29
 
30
+ def onStart
31
+ super
32
+ on_start if respond_to?(:on_start)
33
+ end
34
+
30
35
  def onResume
31
36
  super
32
37
  on_resume
@@ -60,6 +65,11 @@
60
65
  super
61
66
  end
62
67
 
68
+ def onBackPressed
69
+ super
70
+ finish if fragmentManager.getBackStackEntryCount == 0
71
+ end
72
+
63
73
  def open(screen, options={})
64
74
  find.screen.open screen, options
65
75
  end
@@ -68,6 +78,11 @@
68
78
  find.screen.close options
69
79
  end
70
80
 
81
+ def set_content layout_xml
82
+ layout_id = find.resource.layout(layout_xml)
83
+ setContentView(layout_id)
84
+ end
85
+
71
86
  end
72
87
 
73
88
  #end
@@ -20,7 +20,7 @@
20
20
  end
21
21
 
22
22
  def open_fragment(frag, options={})
23
- mp frag
23
+ mp "open fragment: #{frag.inspect}"
24
24
  mgr = fragmentManager.beginTransaction
25
25
  mgr.replace(@fragment_container.getId, frag, "screen-#{fragmentManager.getBackStackEntryCount + 1}")
26
26
  mgr.addToBackStack(nil)
@@ -79,5 +79,12 @@
79
79
  self.contentView = @fragment_container
80
80
  end
81
81
 
82
+ # Pass Acitivity result on to the Fragment/Screen
83
+ def on_activity_result(request_code, result_code, data)
84
+ if fragment && fragment.respond_to?(:activity_result)
85
+ fragment.activity_result(request_code, result_code, data)
86
+ end
87
+ end
88
+
82
89
  end
83
90
  #end
@@ -55,7 +55,7 @@
55
55
  end
56
56
 
57
57
  def on_activity_result(request_code, result_code, data)
58
- if @fragment && @fragment.respond_to?(:handle_activity_result)
58
+ if @fragment && @fragment.respond_to?(:activity_result)
59
59
  @fragment.activity_result(request_code, result_code, data)
60
60
  end
61
61
  end
@@ -3,7 +3,7 @@ class PMBaseAdapter < Android::Widget::BaseAdapter
3
3
 
4
4
  def initialize(opts={})
5
5
  super()
6
- @data = opts.fetch(:data, [])
6
+ self.data = opts.fetch(:data, [])
7
7
  end
8
8
 
9
9
  def screen
@@ -35,12 +35,24 @@ class PMBaseAdapter < Android::Widget::BaseAdapter
35
35
 
36
36
  def getViewTypeCount(); view_type_count; end
37
37
  def view_type_count()
38
- 1
38
+ # all custom items added up (+1 for non-custom)
39
+ view_types.length + 1
39
40
  end
40
41
 
41
42
  def getItemViewType(position); item_view_type_id(position); end
42
43
  def item_view_type_id(position)
43
- 0
44
+ data_item = self.item_data(position)
45
+ idx = nil
46
+ if data_item[:prevent_reuse]
47
+ idx = Android::Widget::Adapter::IGNORE_ITEM_VIEW_TYPE
48
+ else
49
+ # get custom cell index
50
+ idx = view_types.index(data_item[:cell_xml] || data_item[:cell_class])
51
+ # Shift custom cells up 1, no custom == index 0
52
+ idx = idx ? (idx + 1) : 0
53
+ end
54
+
55
+ idx
44
56
  end
45
57
 
46
58
  def getCount(); count(); end
@@ -48,8 +60,8 @@ class PMBaseAdapter < Android::Widget::BaseAdapter
48
60
  data.length
49
61
  end
50
62
 
51
- def getItem(position); item(position); end
52
- def item(position)
63
+ def getItem(position); item_data(position); end
64
+ def item_data(position)
53
65
  data[position]
54
66
  end
55
67
 
@@ -60,22 +72,74 @@ class PMBaseAdapter < Android::Widget::BaseAdapter
60
72
 
61
73
  def getView(position, convert_view, parent); view(position, convert_view, parent); end
62
74
  def view(position, convert_view, parent)
63
- data = item(position)
64
- out = convert_view || rmq.create!(data[:cell_class] || Potion::TextView)
65
- update_view(out, data[:title])
75
+ data = item_data(position)
76
+ out = selected_view(convert_view, data)
77
+ update_view(out, data)
66
78
  if data[:action]
67
- find(out).on(:tap) { find.screen.send(data[:action], data[:arguments], position) }
79
+ find(out).on(:tap) do
80
+ arguments = action_arguments data, position
81
+ find.screen.send(data[:action], arguments, position)
82
+ end
68
83
  end
69
84
  out
70
85
  end
71
86
 
87
+ # configure what to pass back when we tap that action
88
+ def action_arguments(data, position)
89
+ data[:arguments]
90
+ end
91
+
72
92
  def update_view(view, data)
73
- if cell_options[:update].is_a?(Proc)
74
- cell_options[:update].call(out, data)
75
- elsif cell_options[:update].is_a?(Symbol) || cell_options[:update].is_a?(String)
76
- find.screen.send(cell_options[:update], out, data)
77
- else
78
- out.text = data
93
+ update = data[:update]
94
+ if update.is_a?(Proc)
95
+ update.call(out, data)
96
+ elsif update.is_a?(Symbol) || update.is_a?(String)
97
+ if find.screen.respond_to?(update)
98
+ find.screen.send(update, view, data)
99
+ else
100
+ mp "Warning: #{find.screen.class} does not respond to #{update}"
101
+ end
102
+ elsif data[:properties]
103
+ data[:properties].each do |k, v|
104
+ if view.respond_to?("#{k}=")
105
+ view.send("#{k}=", v)
106
+ else
107
+ mp "Warning: #{view.class} does not respond to #{k}="
108
+ end
109
+ end
110
+ elsif view.is_a?(Potion::TextView)
111
+ # Specific to use of Simple list item 1
112
+ view.text = data[:title]
113
+ elsif update
114
+ mp "We don't know how to update your cell"
79
115
  end
80
116
  end
117
+
118
+ def view_types
119
+ # unique cell_xmls and cell_classes
120
+ data.map{ |i| i[:cell_xml] || i[:cell_class]}.compact.uniq
121
+ end
122
+
123
+ def selected_view(cv, data)
124
+ row_view = cv
125
+ unless row_view
126
+ if data[:cell_class]
127
+ row_view = rmq.create!(data[:cell_class])
128
+ elsif data[:cell_xml]
129
+ row_view = inflate_row(data[:cell_xml])
130
+ rmq.tag_all_from_resource_entry_name(row_view)
131
+ else
132
+ # Default is Sipmle List Item 1
133
+ # TODO: Possibly use Android::R::Layout::Simple_list_item_2 which has subtitle
134
+ #https://android.googlesource.com/platform/frameworks/base/+/master/core/res/res/layout/simple_list_item_2.xml
135
+ row_view = inflate_row(Android::R::Layout::Simple_list_item_1)
136
+ end
137
+ end
138
+ row_view
139
+ end
140
+
141
+ def inflate_row(xml_resource)
142
+ inflater = Potion::LayoutInflater.from(find.activity)
143
+ row_view = inflater.inflate(xml_resource, nil, true)
144
+ end
81
145
  end