1 # This file is part of NIT (http://www.nitlanguage.org).
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Views and services to use the Android native user interface
19 # Events, such as a button click, come from the UI thread and are then
20 # passed to the main thread. It is recommended to specialize one of the
21 # methods of the main thread to customize the response to a given event.
23 # This graph shows the path of a button click:
25 # UI Thread # Main thread
30 # Button::click_ui --> Button::click
35 module ui
is min_api_version
14
37 import native_app_glue
38 import pthreads
::concurrent_collections
41 import android.app.NativeActivity;
43 import android.view.Gravity;
44 import android.view.MotionEvent;
45 import android.view.ViewGroup;
46 import android.view.ViewGroup.MarginLayoutParams;
48 import android.widget.Button;
49 import android.widget.LinearLayout;
50 import android.widget.GridLayout;
51 import android.widget.PopupWindow;
52 import android.widget.TextView;
58 # An event from the `app.nit` framework
60 # Reaction to this event
64 # A control click event
68 # Sender of this event
71 redef fun react
do sender
.click
self
74 # Receiver of events not handled directly by the sender
75 interface EventCatcher
76 fun catch_event
(event
: AppEvent) do end
82 # Queue of events to be received by the main thread
83 var event_queue
= new ConcurrentList[AppEvent]
85 # Call `react` on all `AppEvent` available in `event_queue`
86 protected fun loop_on_ui_callbacks
88 var queue
= event_queue
89 while not queue
.is_empty
do
98 # Process Android events
101 # Process app.nit events
107 redef extern class NativeActivity
109 # Fill this entire `NativeActivity` with `popup`
111 # This is a workaround for the use on `takeSurface` in `NativeActivity.java`
113 # TODO replace NativeActivity by our own NitActivity
114 private fun dedicate_to_popup
(popup
: NativePopupWindow, popup_layout
: NativeViewGroup) in "Java" `{
115 final LinearLayout final_main_layout = new LinearLayout(recv);
116 final ViewGroup final_popup_layout = popup_layout;
117 final PopupWindow final_popup = popup;
118 final NativeActivity final_recv = recv;
120 recv.runOnUiThread(new Runnable() {
123 MarginLayoutParams params = new MarginLayoutParams(
124 LinearLayout.LayoutParams.MATCH_PARENT,
125 LinearLayout.LayoutParams.MATCH_PARENT);
127 final_recv.setContentView(final_main_layout, params);
129 final_popup.showAtLocation(final_popup_layout, Gravity.TOP, 0, 40);
134 # Set the main layout of this activity
135 fun content_view
=(layout
: NativeViewGroup)
137 var popup
= new NativePopupWindow(self)
138 popup
.content_view
= layout
139 dedicate_to_popup
(popup
, layout
)
142 # Set the real content view of this activity, without hack
144 # TODO bring use this instead of the hack with `dedicate_to_pupup`
145 private fun real_content_view
=(layout
: NativeViewGroup) in "Java" `{
146 final ViewGroup final_layout = layout;
147 final NativeActivity final_recv = recv;
149 recv.runOnUiThread(new Runnable() {
152 final_recv.setContentView(final_layout);
154 final_layout.requestFocus();
160 # An `Object` that raises events
161 abstract class Eventful
162 var event_catcher
: EventCatcher = app
is lazy
, writable
166 ## Nity classes and services
169 # An Android control with text
170 abstract class TextView
174 # Native Java variant to this Nity class
175 type NATIVE: NativeTextView
177 # The native Java object encapsulated by `self`
178 var native
: NATIVE is noinit
180 # Get the text of this view
183 var jstr
= native
.text
185 jstr
.delete_local_ref
189 # Set the text of this view
190 fun text
=(value
: Text)
192 var jstr
= value
.to_s
.to_java_string
194 jstr
.delete_local_ref
197 # Get whether this view is enabled or not
198 fun enabled
: Bool do return native
.enabled
200 # Set if this view is enabled
201 fun enabled
=(val
: Bool) do native
.enabled
= val
203 # Set the size of the text in this view at `dpi`
204 fun text_size
=(dpi
: Numeric) do native
.text_size
= dpi
.to_f
206 private var finalized
= false
209 if not finalized
then
210 native
.delete_global_ref
220 redef type NATIVE: NativeButton
224 var native
= new NativeButton(app
.native_activity
, app
.event_queue
, self)
225 self.native
= native
.new_global_ref
228 # Click event on the Main thread
230 # By default, this method calls `app.catch_event`. It can be specialized
231 # with custom behavior or the receiver of `catch_event` can be changed
232 # with `event_catcher=`.
233 fun click
(event
: AppEvent) do event_catcher
.catch_event
(event
)
235 # Click event on the UI thread
237 # This method is called on the UI thread and redirects the event to `click`
238 # throught `App::event_queue`. In most cases, you should implement `click`
239 # and leave `click_ui` as is.
240 fun click_ui
do app
.event_queue
.add
(new ClickEvent(self))
243 # An Android editable text field
247 redef type NATIVE: NativeEditText
251 var native
= new NativeEditText(app
.native_activity
)
252 self.native
= native
.new_global_ref
260 # A `View` for Android
261 extern class NativeView in "Java" `{ android.view.View `}
264 fun minimum_width=(val: Int) in "Java" `{ recv.setMinimumWidth((int)val); `}
265 fun minimum_height
=(val
: Int) in "Java" `{ recv.setMinimumHeight((int)val); `}
268 # A collection of `NativeView`
269 extern class NativeViewGroup in "Java" `{ android.view.ViewGroup `}
272 fun add_view
(view
: NativeView) in "Java" `{ recv.addView(view); `}
275 # A `NativeViewGroup` organized in a line
276 extern class NativeLinearLayout in "Java" `{ android.widget.LinearLayout `}
277 super NativeViewGroup
279 new(context
: NativeActivity) in "Java" `{ return new LinearLayout(context); `}
281 fun set_vertical in "Java" `{ recv.setOrientation(LinearLayout.VERTICAL); `}
282 fun set_horizontal
in "Java" `{ recv.setOrientation(LinearLayout.HORIZONTAL); `}
284 redef fun add_view(view) in "Java"
286 MarginLayoutParams params
= new MarginLayoutParams(
287 LinearLayout.LayoutParams.MATCH_PARENT,
288 LinearLayout.LayoutParams.WRAP_CONTENT);
289 recv
.addView
(view
, params
);
292 fun add_view_with_weight(view: NativeView, weight: Float)
294 recv
.addView
(view
, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, (float
)weight
));
298 # A `NativeViewGroup` organized as a grid
299 extern class NativeGridLayout in "Java" `{ android.widget.GridLayout `}
300 super NativeViewGroup
302 new(context
: NativeActivity) in "Java" `{ return new android.widget.GridLayout(context); `}
304 fun row_count=(val: Int) in "Java" `{ recv.setRowCount((int)val); `}
306 fun column_count
=(val
: Int) in "Java" `{ recv.setColumnCount((int)val); `}
308 redef fun add_view(view) in "Java" `{ recv.addView(view); `}
311 extern class NativePopupWindow in "Java" `{ android.widget.PopupWindow `}
314 new (context: NativeActivity) in "Java" `{
315 PopupWindow recv
= new PopupWindow(context
);
316 recv
.setWindowLayoutMode
(LinearLayout.LayoutParams.MATCH_PARENT,
317 LinearLayout.LayoutParams.MATCH_PARENT);
318 recv
.setClippingEnabled
(false);
322 fun content_view=(layout: NativeViewGroup) in "Java" `{ recv.setContentView(layout); `}
325 extern class NativeTextView in "Java" `{ android.widget.TextView `}
328 new (context: NativeActivity) in "Java" `{ return new TextView(context); `}
330 fun text
: JavaString in "Java" `{ return recv.getText().toString(); `}
332 fun text=(value: JavaString) in "Java" `{
334 android
.util
.Log.d
("Nity", "1");
335 final
TextView final_recv
= recv
;
336 final
String final_value
= value
;
338 android
.util
.Log.d
("Nity", "4");
339 ((NativeActivity)recv
.getContext
()).runOnUiThread
(new Runnable() {
342 android
.util
.Log.d
("Nity", "-5");
343 android
.util
.Log.d
("Nity", final_value
);
344 android
.util
.Log.d
("Nity", "-5.5");
345 final_recv
.setText
(final_value
);
346 android
.util
.Log.d
("Nity", "-6");
349 android
.util
.Log.d
("Nity", "7");
352 fun enabled: Bool in "Java" `{ return recv.isEnabled(); `}
353 fun enabled
=(value
: Bool) in "Java" `{
354 final TextView final_recv = recv;
355 final boolean final_value = value;
357 ((NativeActivity)recv.getContext()).runOnUiThread(new Runnable() {
360 final_recv.setEnabled(final_value);
365 fun gravity_center
in "Java" `{
366 recv.setGravity(Gravity.CENTER);
369 fun text_size
=(dpi
: Float) in "Java" `{
370 recv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_DIP, (float)dpi);
374 extern class NativeEditText in "Java" `{ android.widget.EditText `}
377 redef type SELF: NativeEditText
379 new (context: NativeActivity) in "Java" `{ return new android.widget.EditText(context); `}
381 fun width
=(val
: Int) in "Java" `{ recv.setWidth((int)val); `}
383 fun input_type_text in "Java" `{ recv.setInputType(android.text.InputType.TYPE_CLASS_TEXT); `}
385 redef fun new_global_ref
: SELF import sys
, Sys.jni_env
`{
386 Sys sys = NativeEditText_sys(recv);
387 JNIEnv *env = Sys_jni_env(sys);
388 return (*env)->NewGlobalRef(env, recv);
392 extern class NativeButton in "Java" `{ android.widget.Button `}
395 redef type SELF: NativeButton
397 new (context: NativeActivity, queue: ConcurrentList[AppEvent], sender_object: Object) import Button.click_ui in "Java" `{
398 final int final_sender_object
= sender_object
;
400 return new Button(context
){
402 public boolean onTouchEvent
(MotionEvent event
) {
403 if(event
.getAction
() == MotionEvent.ACTION_DOWN) {
404 Button_click_ui(final_sender_object
);
412 redef fun new_global_ref: SELF import sys, Sys.jni_env `{
413 Sys sys
= NativeButton_sys(recv
);
414 JNIEnv *env
= Sys_jni_env(sys
);
415 return (*env
)->NewGlobalRef(env
, recv
);