c009b50a871b8f1ed392710ce5ff891172f89e91
[nit.git] / contrib / benitlux / src / client / android.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
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
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 # Android variant improved with platform specific services
16 module android is
17 android_manifest_activity """android:theme="@android:style/Theme.DeviceDefault" """
18 android_api_min 16 # For BigTextStyle
19 android_api_target 16
20 end
21
22 import ::android::portrait
23 import ::android::toast
24 import ::android::wifi
25 import ::android::service::at_boot
26
27 import client
28 import push
29 import checkins
30
31 redef class App
32
33 redef fun on_create
34 do
35 super
36
37 # Launch service with app, if it wasn't already launched at boot
38 start_service
39 end
40
41 # Use Android toasts if there is an activity, otherwise fallback on the log
42 redef fun feedback(text)
43 do
44 if activities.not_empty then
45 app.toast(text.to_s, false)
46 else super
47 end
48
49 # Register to callback `async_wifi_scan_available` when a wifi scan is available
50 private fun notify_on_wifi_scan(context: NativeContext)
51 import async_wifi_scan_available in "Java" `{
52
53 android.content.IntentFilter filter = new android.content.IntentFilter();
54 filter.addAction(android.net.wifi.WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
55 final int final_self = self;
56
57 context.registerReceiver(
58 new android.content.BroadcastReceiver() {
59 @Override
60 public void onReceive(android.content.Context context, android.content.Intent intent) {
61 if (intent.getAction().equals(android.net.wifi.WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
62 App_async_wifi_scan_available(final_self);
63 }
64 }
65 }, filter);
66 `}
67
68 private fun async_wifi_scan_available do run_on_ui_thread task_on_wifi_scan_available
69
70 private var task_on_wifi_scan_available = new WifiScanAvailable is lazy
71 end
72
73 redef class Service
74 redef fun on_start_command(intent, flags, id)
75 do
76 app.notify_on_wifi_scan native
77
78 # Check token validity
79 (new PushHttpRequest("push/check_token?token={app.token}")).start
80
81 return start_sticky
82 end
83 end
84
85 # Task ran on the UI thread when a wifi scan is available
86 private class WifiScanAvailable
87 super Task
88
89 redef fun main
90 do
91 jni_env.push_local_frame 4
92 var manager = app.native_context.wifi_manager
93 var networks = manager.get_scan_results
94 var found_ben = false
95 for i in networks.length.times do
96 jni_env.push_local_frame 4
97 var net = networks[i]
98 var ssid = net.ssid.to_s
99
100 # TODO use BSSID instead
101 #var bssid = net.bssid.to_s
102 var target_ssids = ["Benelux"]
103 if target_ssids.has(ssid) then # and bssid == "C8:F7:33:81:B0:E6" then
104 found_ben = true
105 break
106 end
107 jni_env.pop_local_frame
108 end
109 jni_env.pop_local_frame
110
111 if found_ben then
112 app.on_check_in
113 else app.on_check_out
114 end
115 end
116
117 redef class SectionTitle
118 init do set_text_style(native, app.native_context)
119
120 private fun set_text_style(view: NativeTextView, context: NativeContext) in "Java" `{
121 view.setTextAppearance(context, android.R.style.TextAppearance_Large);
122 `}
123 end
124
125 redef class ItemView
126 init do set_background(native, app.native_context)
127
128 private fun set_background(view: NativeView, context: NativeContext) in "Java" `{
129 view.setBackgroundResource(R.color.item_background);
130 `}
131 end
132
133 # Use Android notifications
134 redef fun notify(title, content, id)
135 do
136 var service = app.service
137 assert service != null
138 native_notify(service.native, id, title.to_java_string, content.to_java_string)
139 end
140
141 private fun native_notify(context: NativeService, id: Int, title, content: JavaString)
142 in "Java" `{
143 android.app.Notification.BigTextStyle style =
144 new android.app.Notification.BigTextStyle();
145 style.bigText(content);
146
147 android.content.Intent intent = new android.content.Intent(
148 context, nit.app.NitActivity.class);
149 android.app.PendingIntent pendingIntent = android.app.PendingIntent.getActivity(
150 context, 0, intent, android.app.PendingIntent.FLAG_UPDATE_CURRENT);
151
152 android.app.Notification notif = new android.app.Notification.Builder(context)
153 .setContentTitle(title)
154 .setContentText(content)
155 .setSmallIcon(R.drawable.notif)
156 .setAutoCancel(true)
157 .setOngoing(false)
158 .setStyle(style)
159 .setContentIntent(pendingIntent)
160 .setDefaults(android.app.Notification.DEFAULT_SOUND |
161 android.app.Notification.DEFAULT_LIGHTS)
162 .build();
163
164 android.app.NotificationManager notificationManager =
165 (android.app.NotificationManager)context.getSystemService(android.content.Context.NOTIFICATION_SERVICE);
166
167 notificationManager.notify((int)id, notif);
168 `}
169
170
171 # Use `RatingBar` as the beer rating control
172 redef class BeerView
173 redef fun setup_stars(rating)
174 do
175 var title = "Review %0".t.format(beer_info.beer.name).to_java_string
176 native_setup_stars(app.native_context, top_line_layout.native, rating, title, app.user != null)
177 end
178
179 private fun native_setup_stars(context: NativeContext, layout: NativeViewGroup, rating: Int, title: JavaString, loggedin: Bool)
180 import on_review in "Java" `{
181 // Set an indicator/non-interactive display
182 final android.widget.RatingBar view = new android.widget.RatingBar(
183 context, null, android.R.attr.ratingBarStyleIndicator);
184 view.setNumStars(5);
185 view.setRating(rating);
186 view.setIsIndicator(true);
187
188 final android.view.ViewGroup.MarginLayoutParams params = new android.view.ViewGroup.MarginLayoutParams(
189 android.widget.LinearLayout.LayoutParams.WRAP_CONTENT,
190 android.widget.LinearLayout.LayoutParams.FILL_PARENT);
191 layout.addView(view, params);
192
193 // Make some variables final to used in anonymous class and delayed methods
194 final android.content.Context final_context = context;
195 final long final_rating = rating;
196 final String final_title = title;
197 final boolean final_loggedin = loggedin;
198
199 final int final_self = self;
200 BeerView_incr_ref(self); // Nit GC
201
202 view.setOnTouchListener(new android.view.View.OnTouchListener() {
203 @Override
204 public boolean onTouch(android.view.View v, android.view.MotionEvent event) {
205 if (event.getAction() != android.view.MotionEvent.ACTION_UP) return true;
206
207 // Don't show dialog if not logged in
208 if (!final_loggedin) {
209 android.widget.Toast toast = android.widget.Toast.makeText(
210 final_context, "You must login first to post reviews",
211 android.widget.Toast.LENGTH_SHORT);
212 toast.show();
213 return true;
214 }
215
216 // Build dialog with a simple interactive RatingBar
217 final android.app.AlertDialog.Builder dialog_builder = new android.app.AlertDialog.Builder(final_context);
218 final android.widget.RatingBar rating = new android.widget.RatingBar(final_context);
219 rating.setNumStars(5);
220 rating.setStepSize(1.0f);
221 rating.setRating(final_rating);
222
223 // Header bar
224 dialog_builder.setIcon(R.drawable.notif);
225 dialog_builder.setTitle(final_title);
226
227 // Rating control
228 android.widget.LinearLayout l = new android.widget.LinearLayout(final_context);
229 l.addView(rating, params);
230 l.setHorizontalGravity(android.view.Gravity.CENTER_HORIZONTAL);
231 dialog_builder.setView(l);
232
233 // OK button
234 dialog_builder.setPositiveButton(android.R.string.ok,
235 new android.content.DialogInterface.OnClickListener() {
236 public void onClick(android.content.DialogInterface dialog, int which) {
237 dialog.dismiss();
238
239 long r = (long)rating.getRating();
240 view.setRating(r); // Update static control
241 view.invalidate(); // For not refreshing bug
242
243 BeerView_on_review(final_self, r); // Callback
244 BeerView_decr_ref(final_self); // Nit GC
245 }
246 });
247
248 // Cancel button
249 dialog_builder.setNegativeButton(android.R.string.cancel,
250 new android.content.DialogInterface.OnClickListener() {
251 public void onClick(android.content.DialogInterface dialog, int id) {
252 dialog.cancel();
253 BeerView_decr_ref(final_self); // Nit GC
254 }
255 });
256
257 dialog_builder.create();
258 dialog_builder.show();
259 return true;
260 }
261 });
262 `}
263 end