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
17 # `adjustPan` allows to use EditText in a ListLayout
18 android_manifest_activity
"""android:windowSoftInputMode="adjustPan""""
21 # Implementation note:
23 # We cannot rely on `Activity::on_restore_instance_state` to implement
24 # `on_restore_state` is it only invoked if there is a bundled state,
25 # and we don't use the Android bundled state.
32 private import data_store
36 # The Android element used to implement `self`
37 fun native: NATIVE is abstract
40 type NATIVE: JavaObject
43 redef class NativeActivity
45 private fun remove_title_bar in "Java" `{
46 self.requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
49 # Insert a single layout as the root of the activity window
50 private fun insert_root_layout(root_layout_id: Int)
52 android.widget.FrameLayout layout = new android.widget.FrameLayout(self);
53 layout.setId((int)root_layout_id);
54 self.setContentView(layout);
57 # Replace the currently visible fragment, if any, with `native_fragment`
58 private fun show_fragment(root_layout_id: Int, native_fragment: Android_app_Fragment)
60 android.app.FragmentTransaction transaction = self.getFragmentManager().beginTransaction();
61 transaction.replace((int)root_layout_id, native_fragment);
69 app.native_activity.remove_title_bar
70 native_activity.insert_root_layout(root_layout_id)
74 # Identifier of the container holding the fragments
75 private var root_layout_id = 0xFFFF
77 redef fun window=(window)
79 native_activity.show_fragment(root_layout_id, window.native)
83 redef fun on_start do window.on_start
85 redef fun on_destroy do window.on_destroy
88 redef class CompositeControl
89 redef fun on_start do for i in items do i.on_start
91 redef fun on_destroy do for i in items do i.on_destroy
95 redef fun on_back_pressed
97 var window = app.window
98 if window.enable_back_button then
107 # On Android, a window is implemented with the fragment `native`
109 redef var native = (new Android_app_Fragment(self)).new_global_ref
111 redef type NATIVE: Android_app_Fragment
113 # Root high-level view of this window
114 var view: nullable View = null
118 if item isa View then view = item
122 private fun on_create_fragment: NativeView
127 assert view != null else print_error "{class_name} needs a
`view` after
`Window::on_create` returns
"
133 redef type NATIVE: NativeView
135 redef fun enabled=(enabled) do native.enabled = enabled or else true
136 redef fun enabled do return native.enabled
140 redef type NATIVE: NativeViewGroup
148 # FIXME abstract the use either homogeneous or weight to balance views size in a layout
149 native.add_view_with_weight(item.native, 1.0)
152 redef fun remove(item)
155 if item isa View then native.remove_view item.native
159 redef class HorizontalLayout
161 var layout = new NativeLinearLayout(app.native_activity)
162 layout = layout.new_global_ref
163 layout.set_horizontal
168 redef class VerticalLayout
170 var layout = new NativeLinearLayout(app.native_activity)
171 layout = layout.new_global_ref
177 redef class ListLayout
178 redef type NATIVE: Android_widget_ListView
181 var layout = new Android_widget_ListView(app.native_activity)
182 layout = layout.new_global_ref
186 private var adapter: Android_widget_ArrayAdapter do
187 var adapter = new Android_widget_ArrayAdapter(app.native_activity,
188 android_r_layout_simple_list_item_1, self)
189 native.set_adapter adapter
190 return adapter.new_global_ref
196 if item isa View then adapter.add item.native
199 private fun create_view(position: Int): NativeView
201 var ctrl = items[position]
207 redef class Android_widget_ArrayAdapter
208 private new (context: NativeContext, res: Int, sender: ListLayout)
209 import ListLayout.create_view in "Java" `{
210 final int final_sender_object = sender;
211 ListLayout_incr_ref(sender);
213 return new android.widget.ArrayAdapter(context, (int)res) {
215 public android.view.View getView(int position, android.view.View convertView, android.view.ViewGroup parent) {
216 return ListLayout_create_view(final_sender_object, position);
223 redef type NATIVE: NativeTextView
225 redef fun text do return native.text.to_s
226 redef fun text=(value) do
227 if value == null then value = ""
228 native.text = value.to_java_string
231 redef fun size=(size) do set_size_native(app.native_activity, native, size or else 1.0)
233 private fun set_size_native(context: NativeContext, view: NativeTextView, size: Float)
237 s = android.R.style.TextAppearance_Medium;
238 else if (size < 1.0d)
239 s = android.R.style.TextAppearance_Small;
240 else // if (size > 1.0d)
241 s = android.R.style.TextAppearance_Large;
243 view.setTextAppearance(context, s);
246 redef fun align=(align) do set_align_native(native, align or else 0.0)
248 private fun set_align_native(view: NativeTextView, align: Float)
252 g = android.view.Gravity.CENTER_HORIZONTAL;
253 else if (align < 0.5d)
254 g = android.view.Gravity.LEFT;
255 else // if (align > 0.5d)
256 g = android.view.Gravity.RIGHT;
258 view.setGravity(g | android.view.Gravity.CENTER_VERTICAL);
263 redef type NATIVE: NativeTextView
264 redef var native do return (new NativeTextView(app.native_activity)).new_global_ref
268 redef type NATIVE: Android_widget_CompoundButton
269 redef var native do return (new Android_widget_CheckBox(app.native_activity)).new_global_ref
270 init do set_callback_on_toggle(native)
272 redef fun is_checked do return native.is_checked
273 redef fun is_checked=(value) do native.set_checked(value)
275 private fun on_toggle do notify_observers new ToggleEvent(self)
277 private fun set_callback_on_toggle(view: NATIVE)
278 import on_toggle in "Java" `{
279 final int final_sender_object = self;
280 CheckBox_incr_ref(final_sender_object);
282 view.setOnCheckedChangeListener(
283 new android.widget.CompoundButton.OnCheckedChangeListener() {
285 public void onCheckedChanged(android.widget.CompoundButton buttonView, boolean isChecked) {
286 CheckBox_on_toggle(final_sender_object);
292 redef class TextInput
293 redef type NATIVE: NativeEditText
294 redef var native = (new NativeEditText(app.native_activity)).new_global_ref
296 redef fun is_password=(value)
298 native.is_password = value or else false
303 redef class NativeEditText
305 # Configure this view to hide passwords
306 fun is_password=(value: Bool) in "Java" `{
308 self.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
309 self.setTransformationMethod(android.text.method.PasswordTransformationMethod.getInstance());
311 self.setInputType(android.text.InputType.TYPE_CLASS_TEXT);
312 self.setTransformationMethod(null);
320 redef type NATIVE: NativeButton
321 redef var native = (new NativeButton(app.native_activity, self)).new_global_ref
323 private fun on_click do notify_observers new ButtonPressEvent(self)
325 redef fun finalize do native.delete_global_ref
328 redef class NativeButton
329 private new (context: NativeActivity, sender_object: Button)
330 import Button.on_click in "Java" `{
331 final int final_sender_object = sender_object;
332 Button_incr_ref(final_sender_object);
334 return new android.widget.Button(context) {
336 public boolean onTouchEvent(android.view.MotionEvent event) {
337 if (event.getAction() == android.view.MotionEvent.ACTION_UP) {
338 Button_on_click(final_sender_object);
340 } else if (event.getAction() == android.view.MotionEvent.ACTION_DOWN) {
349 redef class Android_app_Fragment
350 private new (nit_window: Window)
351 import Window.on_create_fragment in "Java" `{
352 final int final_nit_window = nit_window;
353 Window_incr_ref(nit_window);
355 return new android.app.Fragment(){
357 public android.view.View onCreateView(android.view.LayoutInflater inflater,
358 android.view.ViewGroup container, android.os.Bundle state) {
360 return Window_on_create_fragment(final_nit_window);
367 redef fun open_in_browser
368 do to_java_string.native_open_in_browser(app.native_activity)
371 redef class JavaString
372 private fun native_open_in_browser(context: NativeContext)
374 android.content.Intent intent = new android.content.Intent(
375 android.content.Intent.ACTION_VIEW,
376 android.net.Uri.parse(self));
377 context.startActivity(intent);