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