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) {  			}  		} | 
