diff options
-rw-r--r-- | altosdroid/AndroidManifest.xml.in | 12 | ||||
-rw-r--r-- | altosdroid/Makefile.am | 2 | ||||
-rw-r--r-- | altosdroid/res/layout/device_list.xml | 24 | ||||
-rw-r--r-- | altosdroid/res/layout/idle_mode.xml | 54 | ||||
-rw-r--r-- | altosdroid/res/layout/igniter_status.xml | 38 | ||||
-rw-r--r-- | altosdroid/res/layout/igniters.xml | 48 | ||||
-rw-r--r-- | altosdroid/res/menu/option_menu.xml | 3 | ||||
-rw-r--r-- | altosdroid/res/values/strings.xml | 15 | ||||
-rw-r--r-- | altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java | 80 | ||||
-rw-r--r-- | altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java | 3 | ||||
-rw-r--r-- | altosdroid/src/org/altusmetrum/AltosDroid/IdleModeActivity.java | 130 | ||||
-rw-r--r-- | altosdroid/src/org/altusmetrum/AltosDroid/IgniterActivity.java | 303 | ||||
-rw-r--r-- | altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java | 178 | ||||
-rw-r--r-- | altoslib/AltosIdleMonitor.java | 18 | ||||
-rw-r--r-- | altoslib/AltosIgnite.java | 15 | ||||
-rw-r--r-- | altoslib/AltosLink.java | 16 |
16 files changed, 888 insertions, 51 deletions
diff --git a/altosdroid/AndroidManifest.xml.in b/altosdroid/AndroidManifest.xml.in index 24035796..15b04445 100644 --- a/altosdroid/AndroidManifest.xml.in +++ b/altosdroid/AndroidManifest.xml.in @@ -39,7 +39,7 @@ <uses-permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"/> <!-- Permissions needed to access USB OTG --> - <uses-feature android:name="android.hardware.usb.host" /> + <uses-feature android:name="android.hardware.usb.host" android:required="false" /> <application android:label="@string/app_name" android:icon="@drawable/app_icon" @@ -82,6 +82,16 @@ android:theme="@android:style/Theme.Dialog" android:configChanges="orientation|keyboardHidden" /> + <activity android:name=".IdleModeActivity" + android:label="@string/idle_mode" + android:theme="@android:style/Theme.Dialog" + android:configChanges="orientation|keyboardHidden" /> + + <activity android:name=".IgniterActivity" + android:label="@string/igniters" + android:theme="@android:style/Theme.Dialog" + android:configChanges="orientation|keyboardHidden" /> + <service android:name=".TelemetryService" /> <meta-data android:name="com.google.android.maps.v2.API_KEY" diff --git a/altosdroid/Makefile.am b/altosdroid/Makefile.am index 26e14ee7..295abbc5 100644 --- a/altosdroid/Makefile.am +++ b/altosdroid/Makefile.am @@ -47,7 +47,7 @@ DRAWABLES=\ LAYOUTS=$(LAYOUT_DIR)/*.xml MENUS=$(MENU_DIR)/*.xml VALUES=$(VALUES_DIR)/*.xml -XMLS=$(XML_DIR)/*.xml +XMLS=$(XML_DIR)/*.xml AndroidManifest.xml RES=$(LAYOUTS) $(MENUS) $(VALUES) $(XMLS) diff --git a/altosdroid/res/layout/device_list.xml b/altosdroid/res/layout/device_list.xml index bf295e4c..57e5501e 100644 --- a/altosdroid/res/layout/device_list.xml +++ b/altosdroid/res/layout/device_list.xml @@ -1,17 +1,21 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2009 The Android Open Source Project +<!-- - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + Copyright © 2016 Keith Packard <keithp@keithp.com> - http://www.apache.org/licenses/LICENSE-2.0 + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" diff --git a/altosdroid/res/layout/idle_mode.xml b/altosdroid/res/layout/idle_mode.xml new file mode 100644 index 00000000..d6a05fbb --- /dev/null +++ b/altosdroid/res/layout/idle_mode.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + + Copyright © 2016 Keith Packard <keithp@keithp.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <TextView android:id="@+id/set_callsign_label" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/set_callsign_label" + /> + <EditText android:id="@+id/set_callsign" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:hint="@string/set_callsign_label"/> + <Button android:id="@+id/connect_idle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/connect_idle" + /> + <Button android:id="@+id/disconnect_idle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/disconnect_idle" + /> + <Button android:id="@+id/reboot_idle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/reboot_idle" + /> + <Button android:id="@+id/igniters_idle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/igniters_idle" + /> +</LinearLayout> diff --git a/altosdroid/res/layout/igniter_status.xml b/altosdroid/res/layout/igniter_status.xml new file mode 100644 index 00000000..298f9155 --- /dev/null +++ b/altosdroid/res/layout/igniter_status.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + + Copyright © 2016 Keith Packard <keithp@keithp.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +--> +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + <TextView + android:id="@+id/igniter_status" + android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:padding="10dp" + android:layout_alignParentRight="true" + /> + <TextView + android:id="@+id/igniter_name" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:padding="10dp" + android:layout_alignParentLeft="@+id/igniter_status" + /> +</RelativeLayout> diff --git a/altosdroid/res/layout/igniters.xml b/altosdroid/res/layout/igniters.xml new file mode 100644 index 00000000..350a1e58 --- /dev/null +++ b/altosdroid/res/layout/igniters.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + + Copyright © 2016 Keith Packard <keithp@keithp.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + + <ListView android:id="@+id/igniters" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:fadeScrollbars="false" + android:scrollbars="vertical" + android:choiceMode="singleChoice" + /> + + <ToggleButton android:id="@+id/igniter_arm" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textOn="@string/igniter_armed" + android:textOff="@string/igniter_arm" + /> + + <Button android:id="@+id/igniter_fire" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/igniter_fire" + /> + +</LinearLayout> diff --git a/altosdroid/res/menu/option_menu.xml b/altosdroid/res/menu/option_menu.xml index 7e08c803..4de4a16e 100644 --- a/altosdroid/res/menu/option_menu.xml +++ b/altosdroid/res/menu/option_menu.xml @@ -46,6 +46,9 @@ <item android:id="@+id/delete_track" android:icon="@android:drawable/ic_notification_clear_all" android:title="@string/delete_track"/> + <item android:id="@+id/idle_mode" + android:icon="@android:drawable/ic_menu_preferences" + android:title="@string/idle_mode" /> <item android:id="@+id/quit" android:icon="@android:drawable/ic_menu_close_clear_cancel" android:title="@string/quit" /> diff --git a/altosdroid/res/values/strings.xml b/altosdroid/res/values/strings.xml index e7014fc9..6f761a58 100644 --- a/altosdroid/res/values/strings.xml +++ b/altosdroid/res/values/strings.xml @@ -111,4 +111,19 @@ <string name="preload_radius">Radius</string> <string name="preload_load">Load Map</string> + + <!-- Idle mode --> + <string name="idle_mode">Idle Mode</string> + <string name="set_callsign_label">Callsign</string> + <string name="connect_idle">Connect</string> + <string name="disconnect_idle">Disconnect</string> + <string name="reboot_idle">Reboot</string> + <string name="igniters_idle">Fire Igniters</string> + + <!-- igniters --> + <string name="igniters">Igniters</string> + <string name="igniter_arm">Arm</string> + <string name="igniter_armed">Armed</string> + <string name="igniter_fire">Fire</string> + </resources> diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java index 4f1dcb1a..e5f56a5a 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java @@ -64,12 +64,20 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, public static final int MSG_STATE = 1; public static final int MSG_UPDATE_AGE = 2; + public static final int MSG_IDLE_MODE = 3; + public static final int MSG_IGNITER_STATUS = 4; // Intent request codes public static final int REQUEST_CONNECT_DEVICE = 1; public static final int REQUEST_ENABLE_BT = 2; public static final int REQUEST_PRELOAD_MAPS = 3; public static final int REQUEST_MAP_TYPE = 4; + public static final int REQUEST_IDLE_MODE = 5; + public static final int REQUEST_IGNITERS = 6; + + public static final String EXTRA_IDLE_MODE = "idle_mode"; + public static final String EXTRA_IDLE_RESULT = "idle_result"; + public static final String EXTRA_TELEMETRY_SERVICE = "telemetry_service"; public int map_type = AltosMap.maptype_hybrid; @@ -100,6 +108,8 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, private double frequency; private int telemetry_rate; + private boolean idle_mode = false; + public Location location = null; // Tabs @@ -146,6 +156,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, case MSG_UPDATE_AGE: ad.update_age(); break; + case MSG_IDLE_MODE: + ad.idle_mode = (Boolean) msg.obj; + ad.update_state(null); + break; } } }; @@ -216,8 +230,8 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, switch (telemetry_state.connect) { case TelemetryState.CONNECT_CONNECTED: if (telemetry_state.config != null) { - String str = String.format("S/N %d %6.3f MHz", telemetry_state.config.serial, - telemetry_state.frequency); + String str = String.format("S/N %d %6.3f MHz%s", telemetry_state.config.serial, + telemetry_state.frequency, idle_mode ? " (idle)" : ""); if (telemetry_state.telemetry_rate != AltosLib.ao_telemetry_rate_38400) str = str.concat(String.format(" %d bps", AltosLib.ao_telemetry_rate_values[telemetry_state.telemetry_rate])); @@ -444,7 +458,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, mCallsignView.setText(state.callsign); } if (saved_state == null || state.serial != saved_state.serial) { - mSerialView.setText(String.format("%d", state.serial)); + if (state.serial == AltosLib.MISSING) + mSerialView.setText(""); + else + mSerialView.setText(String.format("%d", state.serial)); } if (saved_state == null || state.flight != saved_state.flight) { if (state.flight == AltosLib.MISSING) @@ -461,7 +478,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, } } if (saved_state == null || state.rssi != saved_state.rssi) { - mRSSIView.setText(String.format("%d", state.rssi)); + if (state.rssi == AltosLib.MISSING) + mRSSIView.setText(""); + else + mRSSIView.setText(String.format("%d", state.rssi)); } } @@ -681,9 +701,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); - AltosDebug.debug("Resume, location is %f,%f\n", - location.getLatitude(), - location.getLongitude()); + if (location != null) + AltosDebug.debug("Resume, location is %f,%f\n", + location.getLatitude(), + location.getLongitude()); update_ui(telemetry_state, saved_state); } @@ -740,6 +761,12 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, if (resultCode == Activity.RESULT_OK) set_map_type(data); break; + case REQUEST_IDLE_MODE: + if (resultCode == Activity.RESULT_OK) + idle_mode(data); + break; + case REQUEST_IGNITERS: + break; } } @@ -798,6 +825,40 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, } } + private void idle_mode(Intent data) { + int type = data.getIntExtra(IdleModeActivity.EXTRA_IDLE_RESULT, -1); + Message msg; + + AltosDebug.debug("intent idle_mode %d", type); + switch (type) { + case IdleModeActivity.IDLE_MODE_CONNECT: + msg = Message.obtain(null, TelemetryService.MSG_MONITOR_IDLE_START); + try { + mService.send(msg); + } catch (RemoteException re) { + } + break; + case IdleModeActivity.IDLE_MODE_DISCONNECT: + msg = Message.obtain(null, TelemetryService.MSG_MONITOR_IDLE_STOP); + try { + mService.send(msg); + } catch (RemoteException re) { + } + break; + case IdleModeActivity.IDLE_MODE_REBOOT: + msg = Message.obtain(null, TelemetryService.MSG_REBOOT); + try { + mService.send(msg); + } catch (RemoteException re) { + } + break; + case IdleModeActivity.IDLE_MODE_IGNITERS: + Intent serverIntent = new Intent(this, IgniterActivity.class); + startActivityForResult(serverIntent, REQUEST_IGNITERS); + break; + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); @@ -1025,6 +1086,11 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener, } return true; + case R.id.idle_mode: + serverIntent = new Intent(this, IdleModeActivity.class); + serverIntent.putExtra(EXTRA_IDLE_MODE, idle_mode); + startActivityForResult(serverIntent, REQUEST_IDLE_MODE); + return true; } return false; } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java index 6bc52d7e..2b10b11f 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java @@ -207,10 +207,9 @@ public abstract class AltosDroidLink extends AltosLink { public void print(String data) { byte[] bytes = data.getBytes(); - AltosDebug.debug("print(): begin"); + AltosDebug.debug(data.replace('\n', '\\')); for (byte b : bytes) putchar(b); - AltosDebug.debug("print(): Wrote bytes: '" + data.replace('\n', '\\') + "'"); } public AltosDroidLink(Handler handler) { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/IdleModeActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/IdleModeActivity.java new file mode 100644 index 00000000..ec12c192 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/IdleModeActivity.java @@ -0,0 +1,130 @@ +/* + * Copyright © 2016 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +import java.util.*; +import org.altusmetrum.AltosDroid.R; + +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.view.View; +import android.view.Window; +import android.view.View.OnClickListener; +import android.widget.*; +import android.widget.AdapterView.*; + +import org.altusmetrum.altoslib_10.*; + +public class IdleModeActivity extends Activity { + private EditText callsign; + private Button connect; + private Button disconnect; + private Button reboot; + private Button igniters; + + public static final String EXTRA_IDLE_MODE = "idle_mode"; + public static final String EXTRA_IDLE_RESULT = "idle_result"; + + public static final int IDLE_MODE_CONNECT = 1; + public static final int IDLE_MODE_REBOOT = 2; + public static final int IDLE_MODE_IGNITERS = 3; + public static final int IDLE_MODE_DISCONNECT = 4; + + private void done(int type) { + AltosPreferences.set_callsign(callsign()); + Intent intent = new Intent(); + intent.putExtra(EXTRA_IDLE_RESULT, type); + setResult(Activity.RESULT_OK, intent); + finish(); + } + + private String callsign() { + return callsign.getEditableText().toString(); + } + + public void connect_idle() { + done(IDLE_MODE_CONNECT); + } + + public void disconnect_idle() { + AltosDebug.debug("Disconnect idle button pressed"); + done(IDLE_MODE_DISCONNECT); + } + + public void reboot_idle() { + done(IDLE_MODE_REBOOT); + } + + public void igniters_idle() { + done(IDLE_MODE_IGNITERS); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Setup the window + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setContentView(R.layout.idle_mode); + + callsign = (EditText) findViewById(R.id.set_callsign); + callsign.setText(new StringBuffer(AltosPreferences.callsign())); + + connect = (Button) findViewById(R.id.connect_idle); + connect.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + connect_idle(); + } + }); + disconnect = (Button) findViewById(R.id.disconnect_idle); + disconnect.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + disconnect_idle(); + } + }); + + boolean idle_mode = getIntent().getBooleanExtra(AltosDroid.EXTRA_IDLE_MODE, false); + + if (idle_mode) + connect.setVisibility(View.GONE); + else + disconnect.setVisibility(View.GONE); + + reboot = (Button) findViewById(R.id.reboot_idle); + reboot.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + reboot_idle(); + } + }); + igniters = (Button) findViewById(R.id.igniters_idle); + igniters.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + igniters_idle(); + } + }); + + // Set result CANCELED incase the user backs out + setResult(Activity.RESULT_CANCELED); + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/IgniterActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/IgniterActivity.java new file mode 100644 index 00000000..a608bba0 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/IgniterActivity.java @@ -0,0 +1,303 @@ +/* + * Copyright © 2016 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +import java.lang.ref.WeakReference; +import java.util.*; +import org.altusmetrum.AltosDroid.R; + +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.*; +import android.graphics.*; +import android.os.*; +import android.view.*; +import android.view.View.*; +import android.widget.*; +import android.widget.AdapterView.*; + +import org.altusmetrum.altoslib_10.*; + +class IgniterItem { + public String name; + public String status; + public LinearLayout igniter_view = null; + public TextView name_view = null; + public TextView status_view = null; + + private void update() { + AltosDebug.debug("update item %s %s", name, status); + if (name_view != null) + name_view.setText(name); + if (status_view != null) + status_view.setText(status); + } + + public void set(String name, String status) { + if (!name.equals(this.name) || !status.equals(this.status)) { + this.name = name; + this.status = status; + update(); + } + } + + public void realize(LinearLayout igniter_view, + TextView name_view, + TextView status_view) { + if (igniter_view != this.igniter_view || + name_view != this.name_view || + status_view != this.status_view) + { + this.igniter_view = igniter_view; + this.name_view = name_view; + this.status_view = status_view; + update(); + } + } + + public IgniterItem() { + AltosDebug.debug("New igniter item"); + } +} + +class IgniterAdapter extends ArrayAdapter<IgniterItem> { + int resource; + int selected_item = -1; + + public IgniterAdapter(Context context, int in_resource) { + super(context, in_resource); + resource = in_resource; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + IgniterItem item = getItem(position); + if (item.igniter_view == null) { + LinearLayout igniter_view = new LinearLayout(getContext()); + String inflater = Context.LAYOUT_INFLATER_SERVICE; + LayoutInflater li = (LayoutInflater) getContext().getSystemService(inflater); + li.inflate(resource, igniter_view, true); + + item.realize(igniter_view, + (TextView) igniter_view.findViewById(R.id.igniter_name), + (TextView) igniter_view.findViewById(R.id.igniter_status)); + AltosDebug.debug("Realize new igniter view"); + } else + AltosDebug.debug("Reuse existing igniter view"); + if (position == selected_item) + item.igniter_view.setBackgroundColor(Color.RED); + else + item.igniter_view.setBackgroundColor(Color.BLACK); + return item.igniter_view; + } +} + +public class IgniterActivity extends Activity { + private ListView igniters_view; + private ToggleButton arm; + private Button fire; + + private HashMap<String,IgniterItem> igniters = new HashMap<String,IgniterItem>();; + + private IgniterAdapter igniters_adapter; + + private boolean is_bound; + private boolean timer_running; + private Messenger service = null; + private final Messenger messenger = new Messenger(new IncomingHandler(this)); + + private Timer timer; + + public static final int IGNITER_QUERY = 1; + public static final int IGNITER_FIRE = 2; + + // The Handler that gets information back from the Telemetry Service + static class IncomingHandler extends Handler { + private final WeakReference<IgniterActivity> igniter_activity; + IncomingHandler(IgniterActivity ia) { igniter_activity = new WeakReference<IgniterActivity>(ia); } + + @Override + public void handleMessage(Message msg) { + IgniterActivity ia = igniter_activity.get(); + + switch (msg.what) { + case AltosDroid.MSG_IGNITER_STATUS: + ia.igniter_status((HashMap <String,Integer>) msg.obj); + break; + } + } + }; + + + private ServiceConnection connection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder binder) { + service = new Messenger(binder); + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been unexpectedly disconnected - process crashed. + service = null; + } + }; + + void doBindService() { + bindService(new Intent(this, TelemetryService.class), connection, Context.BIND_AUTO_CREATE); + is_bound = true; + } + + void doUnbindService() { + if (is_bound) { + // If we have received the service, and hence registered with it, then now is the time to unregister. + unbindService(connection); + is_bound = false; + } + } + + private void done() { + Intent intent = new Intent(); + setResult(Activity.RESULT_OK, intent); + finish(); + } + + private void fire_igniter() { + } + + private void arm_igniter(boolean is_checked) { + } + + private synchronized void timer_tick() { + if (timer_running) + return; + timer_running = true; + try { + Message msg = Message.obtain(null, TelemetryService.MSG_IGNITER_QUERY); + msg.replyTo = messenger; + service.send(msg); + } catch (RemoteException re) { + } + } + + private boolean set_igniter(HashMap <String,Integer> status, String name, String pretty) { + if (!status.containsKey(name)) + return false; + + IgniterItem item; + if (!igniters.containsKey(name)) { + item = new IgniterItem(); + igniters.put(name, item); + igniters_adapter.add(item); + } else + item = igniters.get(name); + + item.set(pretty, AltosIgnite.status_string(status.get(name))); + return true; + } + + private synchronized void igniter_status(HashMap <String,Integer> status) { + timer_running = false; + if (status == null) { + AltosDebug.debug("no igniter status"); + return; + } + set_igniter(status, "drogue", "Apogee"); + set_igniter(status, "main", "Main"); + for (int extra = 0;; extra++) { + String name = String.format("%d", extra); + String pretty = String.format("%c", 'A' + extra); + if (!set_igniter(status, name, pretty)) + break; + } +// if (igniters_adapter.selected_item >= 0) +// igniters_view.setSelection(selected_item); + } + + private class IgniterItemClickListener implements ListView.OnItemClickListener { + @Override + public void onItemClick(AdapterView<?> av, View v, int position, long id) { + if (igniters_adapter.selected_item >= 0) + igniters_view.setItemChecked(igniters_adapter.selected_item, false); + igniters_view.setItemChecked(position, true); + igniters_adapter.selected_item = position; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Setup the window + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setContentView(R.layout.igniters); + + igniters_view = (ListView) findViewById(R.id.igniters); + igniters_view.setClickable(true); + + igniters_adapter = new IgniterAdapter(this, R.layout.igniter_status); + + igniters_view.setAdapter(igniters_adapter); + igniters_view.setOnItemClickListener(new IgniterItemClickListener()); + + fire = (Button) findViewById(R.id.igniter_fire); + fire.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + fire_igniter(); + } + }); + + arm = (ToggleButton) findViewById(R.id.igniter_arm); + arm.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton v, boolean is_checked) { + arm_igniter(is_checked); + } + }); + + // Set result CANCELED incase the user backs out + setResult(Activity.RESULT_CANCELED); + } + + @Override + protected void onStart() { + super.onStart(); + doBindService(); + } + + @Override + protected void onResume() { + super.onResume(); + timer = new Timer(true); + timer.scheduleAtFixedRate(new TimerTask() { + public void run() { + timer_tick(); + }}, + 1000L, 1000L); + } + + @Override + protected void onPause() { + super.onPause(); + timer.cancel(); + timer = null; + } + + @Override + protected void onStop() { + super.onStop(); + doUnbindService(); + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java index 926109e2..4b26d263 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java @@ -42,7 +42,7 @@ import android.location.Criteria; import org.altusmetrum.altoslib_10.*; -public class TelemetryService extends Service { +public class TelemetryService extends Service implements AltosIdleMonitorListener { static final int MSG_REGISTER_CLIENT = 1; static final int MSG_UNREGISTER_CLIENT = 2; @@ -58,6 +58,11 @@ public class TelemetryService extends Service { static final int MSG_DISCONNECT = 12; static final int MSG_DELETE_SERIAL = 13; static final int MSG_BLUETOOTH_ENABLED = 14; + static final int MSG_MONITOR_IDLE_START= 15; + static final int MSG_MONITOR_IDLE_STOP = 16; + static final int MSG_REBOOT = 17; + static final int MSG_IGNITER_QUERY = 18; + static final int MSG_IGNITER_FIRE = 19; // Unique Identification Number for the Notification. // We use it on Notification start, and to cancel it. @@ -80,6 +85,13 @@ public class TelemetryService extends Service { // Last data seen; send to UI when it starts private TelemetryState telemetry_state; + // Idle monitor if active + AltosIdleMonitor idle_monitor = null; + + // Igniter bits + AltosIgnite ignite = null; + boolean ignite_running; + // Handler of incoming messages from clients. static class IncomingHandler extends Handler { private final WeakReference<TelemetryService> service; @@ -93,6 +105,7 @@ public class TelemetryService extends Service { AltosDroidLink bt = null; if (s == null) return; + switch (msg.what) { /* Messages from application */ @@ -212,6 +225,26 @@ public class TelemetryService extends Service { if (address != null && !address.address.startsWith("USB")) s.start_altos_bluetooth(address, false); break; + case MSG_MONITOR_IDLE_START: + AltosDebug.debug("start monitor idle"); + s.start_idle_monitor(); + break; + case MSG_MONITOR_IDLE_STOP: + AltosDebug.debug("stop monitor idle"); + s.stop_idle_monitor(); + break; + case MSG_REBOOT: + AltosDebug.debug("reboot"); + s.reboot_remote(); + break; + case MSG_IGNITER_QUERY: + AltosDebug.debug("igniter query"); + s.igniter_query(msg.replyTo); + break; + case MSG_IGNITER_FIRE: + AltosDebug.debug("igniter fire"); + s.igniter_fire((String) msg.obj); + break; default: super.handleMessage(msg); } @@ -255,6 +288,7 @@ public class TelemetryService extends Service { /* On connect, send the current state to the new client */ send_to_client(client); + send_idle_mode_to_client(client); /* If we've got an address from a previous session, then * go ahead and try to reconnect to the device @@ -296,17 +330,29 @@ public class TelemetryService extends Service { send_to_client(client); } - private void disconnect(boolean notify) { - AltosDebug.debug("disconnect(): begin"); - - telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED; - telemetry_state.address = null; + private void send_idle_mode_to_client(Messenger client) { + Message m = Message.obtain(null, AltosDroid.MSG_IDLE_MODE, idle_monitor != null); + try { + client.send(m); + } catch (RemoteException e) { + AltosDebug.error("Client %s disappeared", client.toString()); + remove_client(client); + } + } - if (altos_link != null) - altos_link.closing(); + private void send_idle_mode_to_clients() { + for (Messenger client : clients) + send_idle_mode_to_client(client); + } - stop_receiver_voltage_timer(); + private void telemetry_start() { + if (telemetry_reader == null && idle_monitor == null && !ignite_running) { + telemetry_reader = new TelemetryReader(altos_link, handler); + telemetry_reader.start(); + } + } + private void telemetry_stop() { if (telemetry_reader != null) { AltosDebug.debug("disconnect(): stopping TelemetryReader"); telemetry_reader.interrupt(); @@ -316,6 +362,23 @@ public class TelemetryService extends Service { } telemetry_reader = null; } + } + + private void disconnect(boolean notify) { + AltosDebug.debug("disconnect(): begin"); + + telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED; + telemetry_state.address = null; + + if (idle_monitor != null) + stop_idle_monitor(); + + if (altos_link != null) + altos_link.closing(); + + stop_receiver_voltage_timer(); + + telemetry_stop(); if (telemetry_logger != null) { AltosDebug.debug("disconnect(): stopping TelemetryLogger"); telemetry_logger.stop(); @@ -325,6 +388,7 @@ public class TelemetryService extends Service { AltosDebug.debug("disconnect(): stopping AltosDroidLink"); altos_link.close(); altos_link = null; + ignite = null; } telemetry_state.config = null; if (notify) { @@ -373,14 +437,95 @@ public class TelemetryService extends Service { send_to_clients(); } + private void start_idle_monitor() { + if (altos_link != null && idle_monitor == null) { + telemetry_stop(); + idle_monitor = new AltosIdleMonitor(this, altos_link, true, false); + idle_monitor.set_callsign(AltosPreferences.callsign()); + idle_monitor.start(); + send_idle_mode_to_clients(); + } + } + + private void stop_idle_monitor() { + if (idle_monitor != null) { + try { + idle_monitor.abort(); + } catch (InterruptedException ie) { + } + idle_monitor = null; + telemetry_start(); + send_idle_mode_to_clients(); + } + } + + private void reboot_remote() { + if (altos_link != null) { + stop_idle_monitor(); + try { + altos_link.start_remote(); + altos_link.printf("r eboot\n"); + altos_link.flush_output(); + } catch (TimeoutException te) { + } catch (InterruptedException ie) { + } finally { + try { + altos_link.stop_remote(); + } catch (InterruptedException ie) { + } + } + } + } + + private void ensure_ignite() { + if (ignite == null) + ignite = new AltosIgnite(altos_link, true, false); + } + + private synchronized void igniter_query(Messenger client) { + ensure_ignite(); + HashMap<String,Integer> status_map = null; + ignite_running = true; + try { + stop_idle_monitor(); + try { + status_map = ignite.status(); + } catch (InterruptedException ie) { + AltosDebug.debug("ignite.status interrupted"); + } catch (TimeoutException te) { + AltosDebug.debug("ignite.status timeout"); + } + } finally { + ignite_running = false; + } + Message m = Message.obtain(null, AltosDroid.MSG_IGNITER_STATUS, status_map); + try { + client.send(m); + } catch (RemoteException e) { + } + } + + private synchronized void igniter_fire(String igniter) { + ensure_ignite(); + ignite_running = true; + stop_idle_monitor(); + try { + ignite.fire(igniter); + } catch (InterruptedException ie) { + } finally { + ignite_running = false; + } + } + // Timer for receiver battery voltage monitoring Timer receiver_voltage_timer; private void update_receiver_voltage() { - if (altos_link != null) { + if (altos_link != null && idle_monitor == null && !ignite_running) { try { double voltage = altos_link.monitor_battery(); telemetry_state.receiver_battery = voltage; + send_to_clients(); } catch (InterruptedException ie) { } } @@ -428,8 +573,7 @@ public class TelemetryService extends Service { telemetry_state.connect = TelemetryState.CONNECT_CONNECTED; telemetry_state.address = address; - telemetry_reader = new TelemetryReader(altos_link, handler); - telemetry_reader.start(); + telemetry_start(); AltosDebug.debug("connected TelemetryReader started"); @@ -549,4 +693,14 @@ public class TelemetryService extends Service { public IBinder onBind(Intent intent) { return messenger.getBinder(); } + + /* AltosIdleMonitorListener */ + public void update(AltosState state, AltosListenerState listener_state) { + telemetry_state.states.put(state.serial, state); + telemetry_state.receiver_battery = listener_state.battery; + send_to_clients(); + } + + public void failed() { + } } diff --git a/altoslib/AltosIdleMonitor.java b/altoslib/AltosIdleMonitor.java index d377fbfd..bcf20ef3 100644 --- a/altoslib/AltosIdleMonitor.java +++ b/altoslib/AltosIdleMonitor.java @@ -28,6 +28,7 @@ public class AltosIdleMonitor extends Thread { AltosIdleFetch fetch; boolean remote; + boolean close_on_exit; double frequency; String callsign; @@ -107,18 +108,25 @@ public class AltosIdleMonitor extends Thread { } } catch (InterruptedException ie) { } - try { - link.close(); - } catch (InterruptedException ie) { + if (close_on_exit) { + try { + link.close(); + } catch (InterruptedException ie) { + } } } - public AltosIdleMonitor(AltosIdleMonitorListener in_listener, AltosLink in_link, boolean in_remote) - throws FileNotFoundException, InterruptedException, TimeoutException { + public AltosIdleMonitor(AltosIdleMonitorListener in_listener, AltosLink in_link, boolean in_remote, boolean in_close_on_exit) { listener = in_listener; link = in_link; remote = in_remote; + close_on_exit = in_close_on_exit; listener_state = new AltosListenerState(); fetch = new AltosIdleFetch(link); } + + public AltosIdleMonitor(AltosIdleMonitorListener in_listener, AltosLink in_link, boolean in_remote) { + this(in_listener, in_link, in_remote, true); + } } + diff --git a/altoslib/AltosIgnite.java b/altoslib/AltosIgnite.java index 61873b22..ab9c2da6 100644 --- a/altoslib/AltosIgnite.java +++ b/altoslib/AltosIgnite.java @@ -24,6 +24,7 @@ import java.util.concurrent.*; public class AltosIgnite { AltosLink link; boolean remote; + boolean close_on_exit; boolean link_started; boolean have_npyro = false; int npyro; @@ -180,14 +181,18 @@ public class AltosIgnite { public void close() throws InterruptedException { stop_link(); - link.close(); + if (close_on_exit) + link.close(); link = null; } - public AltosIgnite(AltosLink in_link, boolean in_remote) - throws FileNotFoundException, TimeoutException, InterruptedException { - + public AltosIgnite(AltosLink in_link, boolean in_remote, boolean in_close_on_exit) { link = in_link; remote = in_remote; + close_on_exit = in_close_on_exit; + } + + public AltosIgnite(AltosLink in_link, boolean in_remote) { + this(in_link, in_remote, true); } -}
\ No newline at end of file +} diff --git a/altoslib/AltosLink.java b/altoslib/AltosLink.java index d3c178b7..73bab3ef 100644 --- a/altoslib/AltosLink.java +++ b/altoslib/AltosLink.java @@ -539,15 +539,15 @@ public abstract class AltosLink implements Runnable { if (config_data.has_monitor_battery()) { try { - String[] items = adc(); - for (int i = 0; i < items.length;) { - if (items[i].equals("batt")) { - monitor_batt = Integer.parseInt(items[i+1]); - i += 2; - continue; + String[] items = adc(); + for (int i = 0; i < items.length;) { + if (items[i].equals("batt")) { + monitor_batt = Integer.parseInt(items[i+1]); + i += 2; + continue; + } + i++; } - i++; - } } catch (TimeoutException te) { } } |