1 # This file is part of NIT (http://www.nitlanguage.org).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Views and services to use the Android native user interface
18 # Implementation note:
20 # We cannot rely on `Activity::on_restore_instance_state` to implement
21 # `on_restore_state` is it only invoked if there is a bundled state,
22 # and we don't use the Android bundled state.
29 private import data_store
33 # The Android element used to implement `self`
34 fun native
: NATIVE is abstract
37 type NATIVE: JavaObject
40 redef class NativeActivity
42 private fun remove_title_bar
in "Java" `{
43 self.requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
46 # Insert a single layout as the root of the activity window
47 private fun insert_root_layout
(root_layout_id
: Int)
49 android.widget.FrameLayout layout = new android.widget.FrameLayout(self);
50 layout.setId((int)root_layout_id);
51 self.setContentView(layout);
54 # Replace the currently visible fragment, if any, with `native_fragment`
55 private fun show_fragment
(root_layout_id
: Int, native_fragment
: Android_app_Fragment)
57 android.app.FragmentTransaction transaction = self.getFragmentManager().beginTransaction();
58 transaction.replace((int)root_layout_id, native_fragment);
66 app
.native_activity
.remove_title_bar
67 native_activity
.insert_root_layout
(root_layout_id
)
71 # Identifier of the container holding the fragments
72 private var root_layout_id
= 0xFFFF
74 redef fun window
=(window
)
76 native_activity
.show_fragment
(root_layout_id
, window
.native
)
81 # On Android, a window is implemented with the fragment `native`
83 redef var native
= (new Android_app_Fragment(self)).new_global_ref
85 redef type NATIVE: Android_app_Fragment
87 # Root high-level view of this window
88 var view
: nullable View = null
92 if item
isa View then view
= item
96 private fun on_create_fragment
: NativeView
101 assert view
!= null else print_error
"{class_name} needs a `view` after `Window::on_create` returns"
107 redef type NATIVE: NativeView
109 redef fun enabled
=(enabled
) do native
.enabled
= enabled
or else true
110 redef fun enabled
do return native
.enabled
114 redef type NATIVE: NativeViewGroup
122 # FIXME abstract the use either homogeneous or weight to balance views size in a layout
123 native
.add_view_with_weight
(item
.native
, 1.0)
126 redef fun remove
(item
)
129 if item
isa View then native
.remove_view item
.native
133 redef class HorizontalLayout
135 var layout
= new NativeLinearLayout(app
.native_activity
)
136 layout
= layout
.new_global_ref
137 layout
.set_horizontal
142 redef class VerticalLayout
144 var layout
= new NativeLinearLayout(app
.native_activity
)
145 layout
= layout
.new_global_ref
151 redef class ListLayout
152 redef type NATIVE: Android_widget_ListView
155 var layout
= new Android_widget_ListView(app
.native_activity
)
156 layout
= layout
.new_global_ref
160 private var adapter
: Android_widget_ArrayAdapter do
161 var adapter
= new Android_widget_ArrayAdapter(app
.native_activity
,
162 android_r_layout_simple_list_item_1
, self)
163 native
.set_adapter adapter
164 return adapter
.new_global_ref
170 if item
isa View then adapter
.add item
.native
173 private fun create_view
(position
: Int): NativeView
175 var ctrl
= items
[position
]
181 redef class Android_widget_ArrayAdapter
182 private new (context
: NativeContext, res
: Int, sender
: ListLayout)
183 import ListLayout.create_view
in "Java" `{
184 final int final_sender_object = sender;
186 return new android.widget.ArrayAdapter(context, (int)res) {
188 public android.view.View getView(int position, android.view.View convertView, android.view.ViewGroup parent) {
189 return ListLayout_create_view(final_sender_object, position);
196 redef type NATIVE: NativeTextView
198 redef fun text
do return native
.text
.to_s
199 redef fun text
=(value
) do
200 if value
== null then value
= ""
201 native
.text
= value
.to_java_string
204 redef fun size
=(size
) do set_size_native
(app
.native_activity
, native
, size
or else 1.0)
206 private fun set_size_native
(context
: NativeContext, view
: NativeTextView, size
: Float)
210 s = android.R.style.TextAppearance_Medium;
211 else if (size < 1.0d)
212 s = android.R.style.TextAppearance_Small;
213 else // if (size > 1.0d)
214 s = android.R.style.TextAppearance_Large;
216 view.setTextAppearance(context, s);
219 redef fun align
=(align
) do set_align_native
(native
, align
or else 0.0)
221 private fun set_align_native
(view
: NativeTextView, align
: Float)
225 g = android.view.Gravity.CENTER_HORIZONTAL;
226 else if (align < 0.5d)
227 g = android.view.Gravity.LEFT;
228 else // if (align > 0.5d)
229 g = android.view.Gravity.RIGHT;
236 redef type NATIVE: NativeTextView
237 redef var native
do return (new NativeTextView(app
.native_activity
)).new_global_ref
241 redef type NATIVE: Android_widget_CompoundButton
242 redef var native
do return (new Android_widget_CheckBox(app
.native_activity
)).new_global_ref
244 redef fun is_checked
do return native
.is_checked
245 redef fun is_checked
=(value
) do native
.set_checked
(value
)
248 redef class TextInput
249 redef type NATIVE: NativeEditText
250 redef var native
= (new NativeEditText(app
.native_activity
)).new_global_ref
252 redef fun is_password
=(value
)
254 native
.is_password
= value
or else false
259 redef class NativeEditText
261 # Configure this view to hide passwords
262 fun is_password
=(value
: Bool) in "Java" `{
264 self.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
265 self.setTransformationMethod(android.text.method.PasswordTransformationMethod.getInstance());
267 self.setInputType(android.text.InputType.TYPE_CLASS_TEXT);
268 self.setTransformationMethod(null);
276 redef type NATIVE: NativeButton
277 redef var native
= (new NativeButton(app
.native_activity
, self)).new_global_ref
279 private fun on_click
do notify_observers
new ButtonPressEvent(self)
281 redef fun finalize
do native
.delete_global_ref
284 redef class NativeButton
285 private new (context
: NativeActivity, sender_object
: Button)
286 import Button.on_click
in "Java" `{
287 final int final_sender_object = sender_object;
288 Button_incr_ref(final_sender_object);
290 return new android.widget.Button(context){
292 public boolean onTouchEvent(android.view.MotionEvent event) {
293 if(event.getAction() == android.view.MotionEvent.ACTION_DOWN) {
294 Button_on_click(final_sender_object);
303 redef class Android_app_Fragment
304 private new (nit_window
: Window)
305 import Window.on_create_fragment
in "Java" `{
306 final int final_nit_window = nit_window;
308 return new android.app.Fragment(){
310 public android.view.View onCreateView(android.view.LayoutInflater inflater,
311 android.view.ViewGroup container, android.os.Bundle state) {
313 return Window_on_create_fragment(final_nit_window);