altoslib,altosuilib: Bump library version numbers
[fw/altos] / altosdroid / src / org / altusmetrum / AltosDroid / IgniterActivity.java
1 /*
2  * Copyright © 2016 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 package org.altusmetrum.AltosDroid;
20
21 import java.lang.ref.WeakReference;
22 import java.util.*;
23 import org.altusmetrum.AltosDroid.R;
24
25 import android.app.Activity;
26 import android.bluetooth.BluetoothAdapter;
27 import android.bluetooth.BluetoothDevice;
28 import android.content.*;
29 import android.graphics.*;
30 import android.os.*;
31 import android.view.*;
32 import android.view.View.*;
33 import android.widget.*;
34 import android.widget.AdapterView.*;
35
36 import org.altusmetrum.altoslib_12.*;
37
38 class IgniterItem {
39         public String name;
40         public String pretty;
41         public String status;
42         public LinearLayout igniter_view = null;
43         public TextView pretty_view = null;
44         public TextView status_view = null;
45
46         private void update() {
47                 if (pretty_view != null)
48                         pretty_view.setText(pretty);
49                 if (status_view != null)
50                         status_view.setText(status);
51         }
52
53         public void set(String name, String pretty, String status) {
54                 if (!name.equals(this.name) ||
55                     !pretty.equals(this.pretty) ||
56                     !status.equals(this.status))
57                 {
58                         this.name = name;
59                         this.pretty = pretty;
60                         this.status = status;
61                         update();
62                 }
63         }
64
65         public void realize(LinearLayout igniter_view,
66                             TextView pretty_view,
67                             TextView status_view) {
68                 if (igniter_view != this.igniter_view ||
69                     pretty_view != this.pretty_view ||
70                     status_view != this.status_view)
71                 {
72                         this.igniter_view = igniter_view;
73                         this.pretty_view = pretty_view;
74                         this.status_view = status_view;
75                         update();
76                 }
77         }
78
79         public IgniterItem() {
80         }
81 }
82
83 class IgniterAdapter extends ArrayAdapter<IgniterItem> {
84         int resource;
85         int selected_item = -1;
86
87         public IgniterAdapter(Context context, int in_resource) {
88                 super(context, in_resource);
89                 resource = in_resource;
90         }
91
92         @Override
93         public View getView(int position, View convertView, ViewGroup parent) {
94                 IgniterItem item = getItem(position);
95                 if (item.igniter_view == null) {
96                         LinearLayout igniter_view = new LinearLayout(getContext());
97                         String inflater = Context.LAYOUT_INFLATER_SERVICE;
98                         LayoutInflater li = (LayoutInflater) getContext().getSystemService(inflater);
99                         li.inflate(resource, igniter_view, true);
100
101                         item.realize(igniter_view,
102                                      (TextView) igniter_view.findViewById(R.id.igniter_name),
103                                      (TextView) igniter_view.findViewById(R.id.igniter_status));
104                 }
105                 if (position == selected_item)
106                         item.igniter_view.setBackgroundColor(Color.RED);
107                 else
108                         item.igniter_view.setBackgroundColor(Color.BLACK);
109                 return item.igniter_view;
110         }
111 }
112
113 public class IgniterActivity extends Activity {
114         private ListView igniters_view;
115         private ToggleButton arm;
116         private Button fire;
117
118         private HashMap<String,IgniterItem> igniters = new HashMap<String,IgniterItem>();;
119
120         private IgniterAdapter igniters_adapter;
121
122         private boolean is_bound;
123         private Messenger service = null;
124         private final Messenger messenger = new Messenger(new IncomingHandler(this));
125
126         private Timer query_timer;
127         private boolean query_timer_running;
128
129         private Timer arm_timer;
130         private int arm_remaining;
131
132         public static final int IGNITER_QUERY = 1;
133         public static final int IGNITER_FIRE = 2;
134
135         // The Handler that gets information back from the Telemetry Service
136         static class IncomingHandler extends Handler {
137                 private final WeakReference<IgniterActivity> igniter_activity;
138                 IncomingHandler(IgniterActivity ia) { igniter_activity = new WeakReference<IgniterActivity>(ia); }
139
140                 @Override
141                 public void handleMessage(Message msg) {
142                         IgniterActivity ia = igniter_activity.get();
143
144                         switch (msg.what) {
145                         case AltosDroid.MSG_IGNITER_STATUS:
146                                 ia.igniter_status((HashMap <String,Integer>) msg.obj);
147                                 break;
148                         }
149                 }
150         };
151
152
153         private ServiceConnection connection = new ServiceConnection() {
154                 public void onServiceConnected(ComponentName className, IBinder binder) {
155                         service = new Messenger(binder);
156                         query_timer_tick();
157                 }
158
159                 public void onServiceDisconnected(ComponentName className) {
160                         // This is called when the connection with the service has been unexpectedly disconnected - process crashed.
161                         service = null;
162                 }
163         };
164
165         void doBindService() {
166                 bindService(new Intent(this, TelemetryService.class), connection, Context.BIND_AUTO_CREATE);
167                 is_bound = true;
168         }
169
170         void doUnbindService() {
171                 if (is_bound) {
172                         // If we have received the service, and hence registered with it, then now is the time to unregister.
173                         unbindService(connection);
174                         is_bound = false;
175                 }
176         }
177
178         private void done() {
179                 Intent intent = new Intent();
180                 setResult(Activity.RESULT_OK, intent);
181                 finish();
182         }
183
184         class FireThread extends Thread {
185                 private final String igniter;
186
187                 @Override
188                 public void run() {
189                         Message msg = Message.obtain(null, TelemetryService.MSG_IGNITER_FIRE, igniter);
190                         try {
191                                 service.send(msg);
192                         } catch (RemoteException re) {
193                         }
194                 }
195
196                 public FireThread(String igniter) {
197                         this.igniter = igniter;
198                 }
199         }
200
201         private void fire_igniter() {
202                 if (igniters_adapter.selected_item >= 0) {
203                         IgniterItem     item = igniters_adapter.getItem(igniters_adapter.selected_item);
204                         FireThread      ft = new FireThread(item.name);
205                         ft.run();
206                         arm.setChecked(false);
207                 }
208         }
209
210         private void arm_igniter(boolean is_checked) {
211                 if (is_checked) {
212                         arm_timer_stop();
213                         arm_timer = new Timer();
214                         arm_remaining = 10;
215                         arm_set_text();
216                         fire.setEnabled(true);
217                         arm_timer.scheduleAtFixedRate(new TimerTask() {
218                                         public void run() {
219                                                 arm_timer_tick();
220                                         }},
221                                 1000L, 1000L);
222                 } else {
223                         arm_timer_stop();
224                         fire.setEnabled(false);
225                 }
226         }
227
228         private synchronized void query_timer_tick() {
229                 if (query_timer_running)
230                         return;
231                 if (service == null)
232                         return;
233                 query_timer_running = true;
234                 Thread thread = new Thread(new Runnable() {
235                                 public void run() {
236                                         try {
237                                                 Message msg = Message.obtain(null, TelemetryService.MSG_IGNITER_QUERY);
238                                                 msg.replyTo = messenger;
239                                                 if (service == null) {
240                                                         synchronized(IgniterActivity.this) {
241                                                                 query_timer_running = false;
242                                                         }
243                                                 } else
244                                                         service.send(msg);
245                                         } catch (RemoteException re) {
246                                                 AltosDebug.debug("igniter query thread failed");
247                                                 synchronized(IgniterActivity.this) {
248                                                         query_timer_running = false;
249                                                 }
250                                         }
251                                 }
252                         });
253                 thread.start();
254         }
255
256         private boolean set_igniter(HashMap <String,Integer> status, String name, String pretty) {
257                 if (!status.containsKey(name))
258                         return false;
259
260                 IgniterItem item;
261                 if (!igniters.containsKey(name)) {
262                         item = new IgniterItem();
263                         igniters.put(name, item);
264                         igniters_adapter.add(item);
265                 } else
266                         item = igniters.get(name);
267
268                 item.set(name, pretty, AltosIgnite.status_string(status.get(name)));
269                 return true;
270         }
271
272         private synchronized void igniter_status(HashMap <String,Integer> status) {
273                 query_timer_running = false;
274                 if (status == null) {
275                         AltosDebug.debug("no igniter status");
276                         return;
277                 }
278                 set_igniter(status, "drogue", "Apogee");
279                 set_igniter(status, "main", "Main");
280                 for (int extra = 0;; extra++) {
281                         String  name = String.format("%d", extra);
282                         String  pretty = String.format("%c", 'A' + extra);
283                         if (!set_igniter(status, name, pretty))
284                                 break;
285                 }
286         }
287
288         private synchronized void arm_timer_stop() {
289                 if (arm_timer != null) {
290                         arm_timer.cancel();
291                         arm_timer = null;
292                 }
293                 arm_remaining = 0;
294         }
295
296         private void arm_set_text() {
297                 String  text = String.format("Armed %d", arm_remaining);
298
299                 if (arm.isChecked())
300                         arm.setText(text);
301                 arm.setTextOn(text);
302         }
303
304         private void arm_timer_tick() {
305                 --arm_remaining;
306                 if (arm_remaining <= 0) {
307                         arm_timer_stop();
308                         runOnUiThread(new Runnable() {
309                                         public void run() {
310                                                 arm.setChecked(false);
311                                                 fire.setEnabled(false);
312                                         }
313                                 });
314                 } else {
315                         runOnUiThread(new Runnable() {
316                                         public void run() {
317                                                 arm_set_text();
318                                         }
319                                 });
320                 }
321         }
322
323         private void select_item(int position) {
324                 if (position != igniters_adapter.selected_item) {
325                         if (igniters_adapter.selected_item >= 0)
326                                 igniters_view.setItemChecked(igniters_adapter.selected_item, false);
327                         if (position >= 0) {
328                                 igniters_view.setItemChecked(position, true);
329                                 arm.setEnabled(true);
330                         } else
331                                 arm.setEnabled(false);
332                         igniters_adapter.selected_item = position;
333                 }
334         }
335
336         private class IgniterItemClickListener implements ListView.OnItemClickListener {
337                 @Override
338                 public void onItemClick(AdapterView<?> av, View v, int position, long id) {
339                         AltosDebug.debug("select %d\n", position);
340                         select_item(position);
341                 }
342         }
343
344         @Override
345         protected void onCreate(Bundle savedInstanceState) {
346                 super.onCreate(savedInstanceState);
347
348                 // Setup the window
349                 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
350                 setContentView(R.layout.igniters);
351
352                 igniters_view = (ListView) findViewById(R.id.igniters);
353                 igniters_view.setClickable(true);
354
355                 igniters_adapter = new IgniterAdapter(this, R.layout.igniter_status);
356
357                 igniters_view.setAdapter(igniters_adapter);
358                 igniters_view.setOnItemClickListener(new IgniterItemClickListener());
359
360                 fire = (Button) findViewById(R.id.igniter_fire);
361                 fire.setEnabled(false);
362                 fire.setOnClickListener(new OnClickListener() {
363                                 public void onClick(View v) {
364                                         fire_igniter();
365                                 }
366                         });
367
368                 arm = (ToggleButton) findViewById(R.id.igniter_arm);
369                 arm.setEnabled(false);
370                 arm.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener() {
371                                 public void onCheckedChanged(CompoundButton v, boolean is_checked) {
372                                         arm_igniter(is_checked);
373                                 }
374                         });
375
376                 // Set result CANCELED incase the user backs out
377                 setResult(Activity.RESULT_CANCELED);
378         }
379
380         @Override
381         protected void onStart() {
382                 super.onStart();
383                 doBindService();
384         }
385
386         @Override
387         protected void onResume() {
388                 super.onResume();
389                 query_timer = new Timer(true);
390                 query_timer.scheduleAtFixedRate(new TimerTask() {
391                                 public void run() {
392                                         query_timer_tick();
393                                 }},
394                         0L, 5000L);
395         }
396
397         @Override
398         protected void onPause() {
399                 super.onPause();
400                 if (query_timer != null) {
401                         query_timer.cancel();
402                         query_timer = null;
403                 }
404                 arm_timer_stop();
405                 arm.setChecked(false);
406                 fire.setEnabled(false);
407         }
408
409         @Override
410         protected void onStop() {
411                 super.onStop();
412                 doUnbindService();
413         }
414 }