bluepotion 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
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