Initial Commit - Copy from Altus Metrum AltOS
116
altosdroid/app/src/main/AndroidManifest.xml.in
Normal file
@@ -0,0 +1,116 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.altusmetrum.AltosDroid">
|
||||
<!-- Google Maps -->
|
||||
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<!-- Permissions needed to access bluetooth -->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<!-- Permissions needed to save Telemetry logs to SD card -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<!-- Permissions needed for GoogleMaps -->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
|
||||
<permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"
|
||||
android:protectionLevel="signature"/>
|
||||
<uses-permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"/>
|
||||
|
||||
<!-- Permissions needed to access USB OTG -->
|
||||
<uses-feature android:name="android.hardware.usb.host" android:required="false" />
|
||||
|
||||
<application android:label="@string/app_name"
|
||||
android:icon="@drawable/app_icon"
|
||||
android:allowBackup="true"
|
||||
android:theme="@style/Medium">
|
||||
|
||||
<activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:launchMode="singleTop"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
|
||||
android:resource="@xml/device_filter" />
|
||||
</activity>
|
||||
|
||||
<activity android:name=".DeviceListActivity"
|
||||
android:label="@string/select_device"
|
||||
android:theme="@android:style/Theme.Dialog"
|
||||
android:configChanges="orientation|keyboardHidden" />
|
||||
|
||||
<activity android:name=".SelectTrackerActivity"
|
||||
android:label="@string/select_tracker"
|
||||
android:theme="@android:style/Theme.Dialog"
|
||||
android:configChanges="orientation|keyboardHidden" />
|
||||
|
||||
<activity android:name=".PreloadMapActivity"
|
||||
android:label="@string/preload_maps"
|
||||
android:theme="@android:style/Theme.Dialog"
|
||||
android:configChanges="orientation|keyboardHidden" />
|
||||
|
||||
<activity android:name=".MapTypeActivity"
|
||||
android:label="@string/map_type"
|
||||
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" />
|
||||
|
||||
<activity android:name=".SetupActivity"
|
||||
android:label="@string/setup"
|
||||
android:theme="@android:style/Theme.Dialog"
|
||||
android:configChanges="orientation" />
|
||||
|
||||
<activity android:name=".ManageFrequenciesActivity"
|
||||
android:label="@string/manage_frequencies"
|
||||
android:theme="@android:style/Theme.Dialog"
|
||||
android:configChanges="orientation|keyboard" />
|
||||
|
||||
<service android:name=".TelemetryService" />
|
||||
|
||||
<meta-data android:name="com.google.android.maps.v2.API_KEY"
|
||||
android:value="@GOOGLEKEY@"/>
|
||||
<meta-data android:name="com.google.android.gms.version"
|
||||
android:value="@integer/google_play_services_version" />
|
||||
</application>
|
||||
</manifest>
|
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright © 2011 Keith Packard <keithp@keithp.com>
|
||||
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.UUID;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.os.Handler;
|
||||
|
||||
public class AltosBluetooth extends AltosDroidLink {
|
||||
|
||||
private ConnectThread connect_thread = null;
|
||||
|
||||
private BluetoothDevice device;
|
||||
private BluetoothSocket socket;
|
||||
private InputStream input;
|
||||
private OutputStream output;
|
||||
private boolean pause;
|
||||
|
||||
// Constructor
|
||||
public AltosBluetooth(BluetoothDevice device, Handler handler, boolean pause) {
|
||||
super(handler);
|
||||
this.device = device;
|
||||
this.handler = handler;
|
||||
this.pause = pause;
|
||||
|
||||
connect_thread = new ConnectThread();
|
||||
connect_thread.start();
|
||||
}
|
||||
|
||||
void connected() {
|
||||
if (closed()) {
|
||||
AltosDebug.debug("connected after closed");
|
||||
return;
|
||||
}
|
||||
|
||||
AltosDebug.check_ui("connected\n");
|
||||
try {
|
||||
synchronized(this) {
|
||||
if (socket != null) {
|
||||
input = socket.getInputStream();
|
||||
output = socket.getOutputStream();
|
||||
super.connected();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
connect_failed();
|
||||
} catch (IOException io) {
|
||||
connect_failed();
|
||||
}
|
||||
}
|
||||
|
||||
private void connect_failed() {
|
||||
if (closed()) {
|
||||
AltosDebug.debug("connect_failed after closed");
|
||||
return;
|
||||
}
|
||||
|
||||
close_device();
|
||||
input = null;
|
||||
output = null;
|
||||
handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED, this).sendToTarget();
|
||||
AltosDebug.error("ConnectThread: Failed to establish connection");
|
||||
}
|
||||
|
||||
void close_device() {
|
||||
BluetoothSocket tmp_socket;
|
||||
|
||||
synchronized(this) {
|
||||
tmp_socket = socket;
|
||||
socket = null;
|
||||
}
|
||||
|
||||
if (tmp_socket != null) {
|
||||
try {
|
||||
tmp_socket.close();
|
||||
} catch (IOException e) {
|
||||
AltosDebug.error("close_socket failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
super.close();
|
||||
input = null;
|
||||
output = null;
|
||||
}
|
||||
|
||||
private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
|
||||
|
||||
private void create_socket(BluetoothDevice device) {
|
||||
|
||||
BluetoothSocket tmp_socket = null;
|
||||
|
||||
AltosDebug.check_ui("create_socket\n");
|
||||
try {
|
||||
tmp_socket = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (socket != null) {
|
||||
AltosDebug.debug("Socket already allocated %s", socket.toString());
|
||||
close_device();
|
||||
}
|
||||
synchronized (this) {
|
||||
socket = tmp_socket;
|
||||
}
|
||||
}
|
||||
|
||||
private class ConnectThread extends Thread {
|
||||
|
||||
public void run() {
|
||||
AltosDebug.debug("ConnectThread: BEGIN (pause %b)", pause);
|
||||
setName("ConnectThread");
|
||||
|
||||
if (pause) {
|
||||
try {
|
||||
Thread.sleep(4000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
create_socket(device);
|
||||
// Always cancel discovery because it will slow down a connection
|
||||
try {
|
||||
BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
|
||||
} catch (Exception e) {
|
||||
AltosDebug.debug("cancelDiscovery exception %s", e.toString());
|
||||
}
|
||||
|
||||
BluetoothSocket local_socket = null;
|
||||
|
||||
synchronized (AltosBluetooth.this) {
|
||||
if (!closed())
|
||||
local_socket = socket;
|
||||
}
|
||||
|
||||
if (local_socket != null) {
|
||||
try {
|
||||
// Make a connection to the BluetoothSocket
|
||||
// This is a blocking call and will only return on a
|
||||
// successful connection or an exception
|
||||
local_socket.connect();
|
||||
} catch (Exception e) {
|
||||
AltosDebug.debug("Connect exception %s", e.toString());
|
||||
try {
|
||||
local_socket.close();
|
||||
} catch (Exception ce) {
|
||||
AltosDebug.debug("Close exception %s", ce.toString());
|
||||
}
|
||||
local_socket = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (local_socket != null) {
|
||||
connected();
|
||||
} else {
|
||||
connect_failed();
|
||||
}
|
||||
|
||||
AltosDebug.debug("ConnectThread: completed");
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void wait_connected() throws InterruptedException, IOException {
|
||||
AltosDebug.check_ui("wait_connected\n");
|
||||
if (input == null && socket != null) {
|
||||
AltosDebug.debug("wait_connected...");
|
||||
wait();
|
||||
AltosDebug.debug("wait_connected done");
|
||||
}
|
||||
if (socket == null)
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
int write(byte[] buffer, int len) {
|
||||
if (output == null)
|
||||
return -1;
|
||||
try {
|
||||
output.write(buffer, 0, len);
|
||||
} catch (IOException ie) {
|
||||
return -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int read(byte[] buffer, int len) {
|
||||
if (input == null)
|
||||
return -1;
|
||||
try {
|
||||
return input.read(buffer, 0, len);
|
||||
} catch (IOException ie) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Stubs of required methods when extending AltosLink
|
||||
public boolean can_cancel_reply() { return false; }
|
||||
public boolean show_reply_timeout() { return true; }
|
||||
public void hide_reply_timeout() { }
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright © 2015 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.*;
|
||||
|
||||
import android.content.*;
|
||||
import android.util.Log;
|
||||
import android.os.*;
|
||||
import android.content.pm.*;
|
||||
|
||||
public class AltosDebug {
|
||||
// Debugging
|
||||
static final String TAG = "AltosDroid";
|
||||
|
||||
static boolean D = true;
|
||||
|
||||
static void init(Context context) {
|
||||
ApplicationInfo app_info = context.getApplicationInfo();
|
||||
|
||||
if ((app_info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
|
||||
Log.d(TAG, "Enable debugging\n");
|
||||
D = true;
|
||||
} else {
|
||||
Log.d(TAG, "Disable debugging\n");
|
||||
D = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void info(String format, Object ... arguments) {
|
||||
Log.i(TAG, String.format(format, arguments));
|
||||
}
|
||||
|
||||
static void debug(String format, Object ... arguments) {
|
||||
if (D)
|
||||
Log.d(TAG, String.format(format, arguments));
|
||||
}
|
||||
|
||||
static void error(String format, Object ... arguments) {
|
||||
Log.e(TAG, String.format(format, arguments));
|
||||
}
|
||||
|
||||
static void trace(String format, Object ... arguments) {
|
||||
error(format, arguments);
|
||||
for (StackTraceElement el : Thread.currentThread().getStackTrace())
|
||||
Log.e(TAG, "\t" + el.toString() + "\n");
|
||||
}
|
||||
|
||||
static void check_ui(String format, Object ... arguments) {
|
||||
if (Looper.myLooper() == Looper.getMainLooper())
|
||||
trace("ON UI THREAD " + format, arguments);
|
||||
}
|
||||
}
|
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright © 2015 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 android.os.Handler;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public abstract class AltosDroidLink extends AltosLink {
|
||||
|
||||
Handler handler;
|
||||
|
||||
Thread input_thread = null;
|
||||
|
||||
public double frequency() {
|
||||
return frequency;
|
||||
}
|
||||
|
||||
public int telemetry_rate() {
|
||||
return telemetry_rate;
|
||||
}
|
||||
|
||||
public void save_frequency() {
|
||||
AltosPreferences.set_frequency(0, frequency);
|
||||
}
|
||||
|
||||
public void save_telemetry_rate() {
|
||||
AltosPreferences.set_telemetry_rate(0, telemetry_rate);
|
||||
}
|
||||
|
||||
Object closed_lock = new Object();
|
||||
boolean closing = false;
|
||||
boolean closed = false;
|
||||
|
||||
public boolean closed() {
|
||||
synchronized(closed_lock) {
|
||||
return closing;
|
||||
}
|
||||
}
|
||||
|
||||
void connected() throws InterruptedException {
|
||||
input_thread = new Thread(this);
|
||||
input_thread.start();
|
||||
|
||||
// Configure the newly connected device for telemetry
|
||||
print("~\nE 0\n");
|
||||
set_monitor(false);
|
||||
AltosDebug.debug("ConnectThread: connected");
|
||||
|
||||
/* Let TelemetryService know we're connected
|
||||
*/
|
||||
handler.obtainMessage(TelemetryService.MSG_CONNECTED, this).sendToTarget();
|
||||
|
||||
/* Notify other waiting threads that we're connected now
|
||||
*/
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
public void closing() {
|
||||
synchronized(closed_lock) {
|
||||
AltosDebug.debug("Marked closing true");
|
||||
closing = true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean actually_closed() {
|
||||
synchronized(closed_lock) {
|
||||
return closed;
|
||||
}
|
||||
}
|
||||
|
||||
abstract void close_device();
|
||||
|
||||
public void close() {
|
||||
AltosDebug.debug("close(): begin");
|
||||
|
||||
closing();
|
||||
|
||||
flush_output();
|
||||
|
||||
synchronized (closed_lock) {
|
||||
AltosDebug.debug("Marked closed true");
|
||||
closed = true;
|
||||
}
|
||||
|
||||
close_device();
|
||||
|
||||
synchronized(this) {
|
||||
|
||||
if (input_thread != null) {
|
||||
AltosDebug.debug("close(): stopping input_thread");
|
||||
try {
|
||||
AltosDebug.debug("close(): input_thread.interrupt().....");
|
||||
input_thread.interrupt();
|
||||
AltosDebug.debug("close(): input_thread.join().....");
|
||||
input_thread.join();
|
||||
} catch (Exception e) {}
|
||||
input_thread = null;
|
||||
}
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
abstract int write(byte[] buffer, int len);
|
||||
|
||||
abstract int read(byte[] buffer, int len);
|
||||
|
||||
private static final int buffer_size = 64;
|
||||
|
||||
private byte[] in_buffer = new byte[buffer_size];
|
||||
private byte[] out_buffer = new byte[buffer_size];
|
||||
private int buffer_len = 0;
|
||||
private int buffer_off = 0;
|
||||
private int out_buffer_off = 0;
|
||||
|
||||
private byte[] debug_chars = new byte[buffer_size];
|
||||
private int debug_off;
|
||||
|
||||
private void debug_input(byte b) {
|
||||
if (b == '\n') {
|
||||
AltosDebug.debug(" " + new String(debug_chars, 0, debug_off));
|
||||
debug_off = 0;
|
||||
} else {
|
||||
if (debug_off < buffer_size)
|
||||
debug_chars[debug_off++] = b;
|
||||
}
|
||||
}
|
||||
|
||||
private void disconnected() {
|
||||
if (closed()) {
|
||||
AltosDebug.debug("disconnected after closed");
|
||||
return;
|
||||
}
|
||||
|
||||
AltosDebug.debug("Sending disconnected message");
|
||||
handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
|
||||
}
|
||||
|
||||
public int getchar() {
|
||||
|
||||
if (actually_closed())
|
||||
return ERROR;
|
||||
|
||||
while (buffer_off == buffer_len) {
|
||||
buffer_len = read(in_buffer, buffer_size);
|
||||
if (buffer_len < 0) {
|
||||
AltosDebug.debug("ERROR returned from getchar()");
|
||||
disconnected();
|
||||
return ERROR;
|
||||
}
|
||||
buffer_off = 0;
|
||||
}
|
||||
// if (AltosDebug.D)
|
||||
// debug_input(in_buffer[buffer_off]);
|
||||
return in_buffer[buffer_off++];
|
||||
}
|
||||
|
||||
public void flush_output() {
|
||||
super.flush_output();
|
||||
|
||||
if (actually_closed()) {
|
||||
out_buffer_off = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
while (out_buffer_off != 0) {
|
||||
int sent = write(out_buffer, out_buffer_off);
|
||||
|
||||
if (sent <= 0) {
|
||||
AltosDebug.debug("flush_output() failed");
|
||||
out_buffer_off = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sent < out_buffer_off)
|
||||
System.arraycopy(out_buffer, 0, out_buffer, sent, out_buffer_off - sent);
|
||||
|
||||
out_buffer_off -= sent;
|
||||
}
|
||||
}
|
||||
|
||||
public void putchar(byte c) {
|
||||
out_buffer[out_buffer_off++] = c;
|
||||
if (out_buffer_off == buffer_size)
|
||||
flush_output();
|
||||
}
|
||||
|
||||
public void print(String data) {
|
||||
byte[] bytes = data.getBytes();
|
||||
// AltosDebug.debug(data.replace('\n', '\\'));
|
||||
for (byte b : bytes)
|
||||
putchar(b);
|
||||
}
|
||||
|
||||
public AltosDroidLink(Handler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright © 2015 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 android.location.Location;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public interface AltosDroidMapInterface {
|
||||
public void onCreateView(AltosDroid altos_droid);
|
||||
|
||||
public void onDestroyView();
|
||||
|
||||
public void set_visible(boolean visible);
|
||||
|
||||
public void center(double lat, double lon, double accuracy);
|
||||
|
||||
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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;
|
||||
|
||||
public interface AltosDroidMapSourceListener {
|
||||
public void map_source_changed(int map_source);
|
||||
}
|
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright © 2014 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 android.content.Context;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosDroidPreferences extends AltosPreferences {
|
||||
|
||||
/* Active device preference name */
|
||||
final static String activeDeviceAddressPreference = "ACTIVE-DEVICE-ADDRESS";
|
||||
final static String activeDeviceNamePreference = "ACTIVE-DEVICE-NAME";
|
||||
|
||||
public static final int font_size_small = 0;
|
||||
public static final int font_size_medium = 1;
|
||||
public static final int font_size_large = 2;
|
||||
public static final int font_size_extra = 3;
|
||||
|
||||
final static String fontSizePreference = "FONT-SIZE";
|
||||
|
||||
static int font_size = font_size_medium;
|
||||
|
||||
static DeviceAddress active_device_address;
|
||||
|
||||
/* Map source preference name */
|
||||
final static String mapSourcePreference = "MAP-SOURCE";
|
||||
|
||||
static final int MAP_SOURCE_OFFLINE = 0;
|
||||
static final int MAP_SOURCE_ONLINE = 1;
|
||||
|
||||
static int map_source;
|
||||
|
||||
/* Tracker sort selection */
|
||||
final static String trackerSortPreference = "TRACKER-SORT";
|
||||
|
||||
static int tracker_sort;
|
||||
|
||||
public static void init(Context context) {
|
||||
if (backend != null)
|
||||
return;
|
||||
|
||||
AltosPreferences.init(new AltosDroidPreferencesBackend(context));
|
||||
|
||||
font_size = backend.getInt(fontSizePreference, font_size_medium);
|
||||
|
||||
String address = backend.getString(activeDeviceAddressPreference, null);
|
||||
String name = backend.getString(activeDeviceNamePreference, null);
|
||||
|
||||
if (address != null && name != null)
|
||||
active_device_address = new DeviceAddress (address, name);
|
||||
|
||||
map_source = backend.getInt(mapSourcePreference, MAP_SOURCE_ONLINE);
|
||||
|
||||
tracker_sort = backend.getInt(trackerSortPreference, 0);
|
||||
}
|
||||
|
||||
public static void set_active_device(DeviceAddress address) {
|
||||
if (backend == null)
|
||||
return;
|
||||
synchronized(backend) {
|
||||
active_device_address = address;
|
||||
if (active_device_address != null) {
|
||||
backend.putString(activeDeviceAddressPreference, active_device_address.address);
|
||||
backend.putString(activeDeviceNamePreference, active_device_address.name);
|
||||
} else {
|
||||
backend.remove(activeDeviceAddressPreference);
|
||||
backend.remove(activeDeviceNamePreference);
|
||||
}
|
||||
flush_preferences();
|
||||
}
|
||||
}
|
||||
|
||||
public static DeviceAddress active_device() {
|
||||
if (backend == null)
|
||||
return null;
|
||||
|
||||
synchronized(backend) {
|
||||
return active_device_address;
|
||||
}
|
||||
}
|
||||
|
||||
static LinkedList<AltosDroidMapSourceListener> map_source_listeners;
|
||||
|
||||
public static void set_map_source(int map_source) {
|
||||
if (backend == null)
|
||||
return;
|
||||
synchronized(backend) {
|
||||
AltosDroidPreferences.map_source = map_source;
|
||||
backend.putInt(mapSourcePreference, map_source);
|
||||
flush_preferences();
|
||||
}
|
||||
if (map_source_listeners != null) {
|
||||
for (AltosDroidMapSourceListener l : map_source_listeners) {
|
||||
l.map_source_changed(map_source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int map_source() {
|
||||
if (backend == null)
|
||||
return MAP_SOURCE_ONLINE;
|
||||
synchronized(backend) {
|
||||
return map_source;
|
||||
}
|
||||
}
|
||||
|
||||
public static void register_map_source_listener(AltosDroidMapSourceListener l) {
|
||||
if (backend == null)
|
||||
return;
|
||||
|
||||
synchronized(backend) {
|
||||
if (map_source_listeners == null)
|
||||
map_source_listeners = new LinkedList<AltosDroidMapSourceListener>();
|
||||
map_source_listeners.add(l);
|
||||
}
|
||||
}
|
||||
|
||||
public static void unregister_map_source_listener(AltosDroidMapSourceListener l) {
|
||||
if (backend == null)
|
||||
return;
|
||||
synchronized(backend) {
|
||||
map_source_listeners.remove(l);
|
||||
}
|
||||
}
|
||||
|
||||
public static int font_size() {
|
||||
if (backend == null)
|
||||
return font_size_medium;
|
||||
|
||||
synchronized (backend) {
|
||||
return font_size;
|
||||
}
|
||||
}
|
||||
|
||||
public static void set_font_size(int new_font_size) {
|
||||
if (backend == null)
|
||||
return;
|
||||
synchronized (backend) {
|
||||
if (font_size != new_font_size) {
|
||||
font_size = new_font_size;
|
||||
backend.putInt(fontSizePreference, font_size);
|
||||
flush_preferences();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static int tracker_sort() {
|
||||
if (backend == null)
|
||||
return 0;
|
||||
|
||||
synchronized(backend) {
|
||||
return tracker_sort;
|
||||
}
|
||||
}
|
||||
|
||||
public static void set_tracker_sort(int new_tracker_sort) {
|
||||
if (backend == null)
|
||||
return;
|
||||
|
||||
synchronized(backend) {
|
||||
if (tracker_sort != new_tracker_sort) {
|
||||
tracker_sort = new_tracker_sort;
|
||||
backend.putInt(trackerSortPreference, tracker_sort);
|
||||
flush_preferences();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.io.File;
|
||||
import java.util.Map;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Environment;
|
||||
import android.util.*;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosDroidPreferencesBackend extends AltosPreferencesBackend {
|
||||
public final static String NAME = "org.altusmetrum.AltosDroid";
|
||||
private Context context = null;
|
||||
private SharedPreferences prefs = null;
|
||||
private SharedPreferences.Editor editor = null;
|
||||
|
||||
public AltosDroidPreferencesBackend(Context in_context) {
|
||||
this(in_context, NAME);
|
||||
}
|
||||
|
||||
public AltosDroidPreferencesBackend(Context in_context, String in_prefs) {
|
||||
context = in_context;
|
||||
prefs = context.getSharedPreferences(in_prefs, 0);
|
||||
editor = prefs.edit();
|
||||
}
|
||||
|
||||
public String[] keys() {
|
||||
Map<String, ?> all = prefs.getAll();
|
||||
Object[] ao = all.keySet().toArray();
|
||||
|
||||
String[] as = new String[ao.length];
|
||||
for (int i = 0; i < ao.length; i++)
|
||||
as[i] = (String) ao[i];
|
||||
return as;
|
||||
}
|
||||
|
||||
public AltosPreferencesBackend node(String key) {
|
||||
if (!nodeExists(key))
|
||||
putBoolean(key, true);
|
||||
return new AltosDroidPreferencesBackend(context, key);
|
||||
}
|
||||
|
||||
public boolean nodeExists(String key) {
|
||||
return prefs.contains(key);
|
||||
}
|
||||
|
||||
public boolean getBoolean(String key, boolean def) {
|
||||
return prefs.getBoolean(key, def);
|
||||
}
|
||||
|
||||
public double getDouble(String key, double def) {
|
||||
Float f = Float.valueOf(prefs.getFloat(key, (float)def));
|
||||
return f.doubleValue();
|
||||
}
|
||||
|
||||
public int getInt(String key, int def) {
|
||||
return prefs.getInt(key, def);
|
||||
}
|
||||
|
||||
public String getString(String key, String def) {
|
||||
String ret;
|
||||
if (key.equals(AltosPreferences.logdirPreference))
|
||||
ret = null;
|
||||
else
|
||||
ret = prefs.getString(key, def);
|
||||
AltosDebug.debug("AltosDroidPreferencesBackend get string %s:\n", key);
|
||||
if (ret == null)
|
||||
AltosDebug.debug(" (null)\n");
|
||||
else {
|
||||
String[] lines = ret.split("\n");
|
||||
for (String l : lines)
|
||||
AltosDebug.debug(" %s\n", l);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public byte[] getBytes(String key, byte[] def) {
|
||||
String save = prefs.getString(key, null);
|
||||
|
||||
if (save == null)
|
||||
return def;
|
||||
|
||||
byte[] bytes = Base64.decode(save, Base64.DEFAULT);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public void putBoolean(String key, boolean value) {
|
||||
editor.putBoolean(key, value);
|
||||
}
|
||||
|
||||
public void putDouble(String key, double value) {
|
||||
editor.putFloat(key, (float)value);
|
||||
}
|
||||
|
||||
public void putInt(String key, int value) {
|
||||
editor.putInt(key, value);
|
||||
}
|
||||
|
||||
public void putString(String key, String value) {
|
||||
// AltosDebug.debug("AltosDroidPreferencesBackend put string %s:\n", key);
|
||||
// String[] lines = value.split("\n");
|
||||
// for (String l : lines)
|
||||
// AltosDebug.debug(" %s\n", l);
|
||||
editor.putString(key, value);
|
||||
}
|
||||
|
||||
public void putBytes(String key, byte[] bytes) {
|
||||
String save = Base64.encodeToString(bytes, Base64.DEFAULT);
|
||||
editor.putString(key, save);
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
AltosDebug.debug("remove preference %s\n", key);
|
||||
editor.remove(key);
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public File homeDirectory() {
|
||||
return context.getExternalMediaDirs()[0];
|
||||
}
|
||||
|
||||
public void debug(String format, Object ... arguments) {
|
||||
AltosDebug.debug(format, arguments);
|
||||
}
|
||||
}
|
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 org.altusmetrum.altoslib_14.*;
|
||||
import android.location.Location;
|
||||
import android.app.Activity;
|
||||
import android.content.*;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import android.widget.TextView;
|
||||
|
||||
public abstract class AltosDroidTab extends Fragment implements AltosUnitsListener {
|
||||
TelemetryState last_telem_state;
|
||||
AltosState last_state;
|
||||
AltosGreatCircle last_from_receiver;
|
||||
Location last_receiver;
|
||||
AltosDroid altos_droid;
|
||||
|
||||
public abstract void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
|
||||
|
||||
public abstract String tab_name();
|
||||
|
||||
public void units_changed(boolean imperial_units) {
|
||||
if (!isHidden())
|
||||
show(last_telem_state, last_state, last_from_receiver, last_receiver);
|
||||
}
|
||||
|
||||
public void set_value(TextView text_view,
|
||||
AltosUnits units,
|
||||
int width,
|
||||
double value) {
|
||||
if (value == AltosLib.MISSING)
|
||||
text_view.setText("");
|
||||
else
|
||||
text_view.setText(units.show(width, value));
|
||||
}
|
||||
|
||||
public void set_visible(boolean visible) {
|
||||
FragmentTransaction ft = AltosDroid.fm.beginTransaction();
|
||||
AltosDebug.debug("set visible %b %s\n", visible, tab_name());
|
||||
if (visible) {
|
||||
ft.show(this);
|
||||
show(last_telem_state, last_state, last_from_receiver, last_receiver);
|
||||
} else
|
||||
ft.hide(this);
|
||||
try {
|
||||
ft.commitAllowingStateLoss();
|
||||
} catch (IllegalStateException ie) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
AltosDebug.debug("tab onAttach %s %s\n", tab_name(), this);
|
||||
super.onAttach(context);
|
||||
altos_droid = (AltosDroid) context;
|
||||
altos_droid.registerTab(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
AltosDebug.debug("tab onDetach %s %s\n", tab_name(), this);
|
||||
super.onDetach();
|
||||
altos_droid.unregisterTab(this);
|
||||
altos_droid = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
AltosDebug.debug("onResume tab %s %s\n", tab_name(), this);
|
||||
set_visible(true);
|
||||
}
|
||||
|
||||
public void update_ui(TelemetryState telem_state, AltosState state,
|
||||
AltosGreatCircle from_receiver, Location receiver, boolean is_current)
|
||||
{
|
||||
AltosDebug.debug("update_ui %s is_current %b\n", tab_name(), is_current);
|
||||
last_telem_state = telem_state;
|
||||
last_state = state;
|
||||
last_from_receiver = from_receiver;
|
||||
last_receiver = receiver;
|
||||
if (is_current)
|
||||
show(telem_state, state, from_receiver, receiver);
|
||||
else
|
||||
return;
|
||||
}
|
||||
}
|
@@ -0,0 +1,527 @@
|
||||
/*
|
||||
* Copyright © 2015 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 java.io.*;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
import android.graphics.*;
|
||||
import android.view.*;
|
||||
import android.location.Location;
|
||||
import android.content.*;
|
||||
import android.util.*;
|
||||
|
||||
class Rocket implements Comparable {
|
||||
AltosLatLon position;
|
||||
String name;
|
||||
int serial;
|
||||
long last_packet;
|
||||
boolean active;
|
||||
AltosMapOffline map_offline;
|
||||
|
||||
void paint() {
|
||||
map_offline.draw_bitmap(position, map_offline.rocket_bitmap, map_offline.rocket_off_x, map_offline.rocket_off_y);
|
||||
map_offline.draw_text(position, name, 0, 3*map_offline.rocket_bitmap.getHeight()/4);
|
||||
}
|
||||
|
||||
void set_position(AltosLatLon position, long last_packet) {
|
||||
this.position = position;
|
||||
this.last_packet = last_packet;
|
||||
}
|
||||
|
||||
void set_active(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public int compareTo(Object o) {
|
||||
Rocket other = (Rocket) o;
|
||||
|
||||
if (active && !other.active)
|
||||
return 1;
|
||||
if (other.active && !active)
|
||||
return -1;
|
||||
|
||||
long diff = last_packet - other.last_packet;
|
||||
|
||||
if (diff > 0)
|
||||
return 1;
|
||||
if (diff < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Rocket(int serial, AltosMapOffline map_offline) {
|
||||
this.serial = serial;
|
||||
this.name = String.format("%d", serial);
|
||||
this.map_offline = map_offline;
|
||||
}
|
||||
}
|
||||
|
||||
public class AltosMapOffline extends View implements ScaleGestureDetector.OnScaleGestureListener, AltosMapInterface, AltosDroidMapInterface, AltosMapTypeListener {
|
||||
ScaleGestureDetector scale_detector;
|
||||
boolean scaling;
|
||||
AltosMap map;
|
||||
AltosDroid altos_droid;
|
||||
|
||||
static int scale = 1;
|
||||
|
||||
AltosLatLon here;
|
||||
AltosLatLon there;
|
||||
AltosLatLon pad;
|
||||
|
||||
Canvas canvas;
|
||||
Paint paint;
|
||||
|
||||
Bitmap pad_bitmap;
|
||||
int pad_off_x, pad_off_y;
|
||||
Bitmap rocket_bitmap;
|
||||
int rocket_off_x, rocket_off_y;
|
||||
Bitmap here_bitmap;
|
||||
int here_off_x, here_off_y;
|
||||
|
||||
static final int WHITE = 0xffffffff;
|
||||
static final int RED = 0xffff0000;
|
||||
static final int PINK = 0xffff8080;
|
||||
static final int YELLOW= 0xffffff00;
|
||||
static final int CYAN = 0xff00ffff;
|
||||
static final int BLUE = 0xff0000ff;
|
||||
static final int BLACK = 0xff000000;
|
||||
|
||||
public static final int stateColors[] = {
|
||||
WHITE, // startup
|
||||
WHITE, // idle
|
||||
WHITE, // pad
|
||||
RED, // boost
|
||||
PINK, // fast
|
||||
YELLOW, // coast
|
||||
CYAN, // drogue
|
||||
BLUE, // main
|
||||
BLACK, // landed
|
||||
BLACK, // invalid
|
||||
CYAN, // stateless
|
||||
};
|
||||
|
||||
/* AltosMapInterface */
|
||||
public void debug(String format, Object ... arguments) {
|
||||
AltosDebug.debug(format, arguments);
|
||||
}
|
||||
|
||||
class MapTile extends AltosMapTile {
|
||||
public void paint(AltosMapTransform t) {
|
||||
AltosPointInt pt = new AltosPointInt(t.screen(upper_left));
|
||||
|
||||
if (canvas.quickReject(pt.x, pt.y, pt.x + px_size, pt.y + px_size, Canvas.EdgeType.AA))
|
||||
return;
|
||||
|
||||
AltosImage altos_image = this.get_image();
|
||||
|
||||
MapImage map_image = (MapImage) altos_image;
|
||||
|
||||
Bitmap bitmap = null;
|
||||
|
||||
if (map_image != null)
|
||||
bitmap = map_image.bitmap;
|
||||
|
||||
if (bitmap != null) {
|
||||
canvas.drawBitmap(bitmap, pt.x, pt.y, paint);
|
||||
} else {
|
||||
paint.setColor(0xff808080);
|
||||
canvas.drawRect(pt.x, pt.y, pt.x + px_size, pt.y + px_size, paint);
|
||||
if (t.has_location()) {
|
||||
String message = null;
|
||||
switch (status) {
|
||||
case AltosMapTile.fetching:
|
||||
message = "Fetching...";
|
||||
break;
|
||||
case AltosMapTile.bad_request:
|
||||
message = "Internal error";
|
||||
break;
|
||||
case AltosMapTile.failed:
|
||||
message = "Network error";
|
||||
break;
|
||||
case AltosMapTile.forbidden:
|
||||
message = "Outside of known launch areas";
|
||||
break;
|
||||
}
|
||||
if (message != null) {
|
||||
Rect bounds = new Rect();
|
||||
paint.getTextBounds(message, 0, message.length(), bounds);
|
||||
|
||||
int width = bounds.right - bounds.left;
|
||||
int height = bounds.bottom - bounds.top;
|
||||
|
||||
float x = pt.x + px_size / 2.0f;
|
||||
float y = pt.y + px_size / 2.0f;
|
||||
x = x - width / 2.0f;
|
||||
y = y + height / 2.0f;
|
||||
paint.setColor(0xff000000);
|
||||
canvas.drawText(message, 0, message.length(), x, y, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MapTile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
|
||||
super(cache, upper_left, center, zoom, maptype, px_size, scale);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AltosMapTile new_tile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
|
||||
return new MapTile(cache, upper_left, center, zoom, maptype, px_size, scale);
|
||||
}
|
||||
|
||||
public AltosMapPath new_path() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public AltosMapLine new_line() {
|
||||
return null;
|
||||
}
|
||||
|
||||
class MapImage implements AltosImage {
|
||||
public Bitmap bitmap;
|
||||
|
||||
public void flush() {
|
||||
if (bitmap != null) {
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
}
|
||||
}
|
||||
|
||||
public MapImage(File file) {
|
||||
bitmap = BitmapFactory.decodeFile(file.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
public AltosImage load_image(File file) throws Exception {
|
||||
return new MapImage(file);
|
||||
}
|
||||
|
||||
class MapMark extends AltosMapMark {
|
||||
public void paint(AltosMapTransform t) {
|
||||
}
|
||||
|
||||
MapMark(double lat, double lon, int state) {
|
||||
super(lat, lon, state);
|
||||
}
|
||||
|
||||
MapMark(double lat, double lon, int state, String label) {
|
||||
super(lat, lon, state, label);
|
||||
}
|
||||
}
|
||||
|
||||
public AltosMapMark new_mark(double lat, double lon, int state) {
|
||||
return new MapMark(lat, lon, state);
|
||||
}
|
||||
|
||||
public AltosMapMark new_mark(double lat, double lon, int state, String label) {
|
||||
return new MapMark(lat, lon, state, label);
|
||||
}
|
||||
|
||||
public int width() {
|
||||
return getWidth();
|
||||
}
|
||||
|
||||
public int height() {
|
||||
return getHeight();
|
||||
}
|
||||
|
||||
public void repaint() {
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
public void repaint(AltosRectangle damage) {
|
||||
postInvalidate(damage.x, damage.y, damage.x + damage.width, damage.y + damage.height);
|
||||
}
|
||||
|
||||
public void set_zoom_label(String label) {
|
||||
}
|
||||
|
||||
public void select_object(AltosLatLon latlon) {
|
||||
if (map.transform == null)
|
||||
return;
|
||||
ArrayList<Integer> near = new ArrayList<Integer>();
|
||||
|
||||
for (Rocket rocket : sorted_rockets()) {
|
||||
if (rocket.position == null) {
|
||||
debug("rocket %d has no position\n", rocket.serial);
|
||||
continue;
|
||||
}
|
||||
double distance = map.transform.hypot(latlon, rocket.position);
|
||||
debug("check select %d distance %g width %d\n", rocket.serial, distance, rocket_bitmap.getWidth());
|
||||
if (distance < rocket_bitmap.getWidth() * 2.0) {
|
||||
debug("selecting %d\n", rocket.serial);
|
||||
near.add(rocket.serial);
|
||||
}
|
||||
}
|
||||
if (near.size() != 0)
|
||||
altos_droid.touch_trackers(near.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
class Line {
|
||||
AltosLatLon a, b;
|
||||
|
||||
void paint() {
|
||||
if (a != null && b != null) {
|
||||
AltosPointDouble a_screen = map.transform.screen(a);
|
||||
AltosPointDouble b_screen = map.transform.screen(b);
|
||||
paint.setColor(0xff8080ff);
|
||||
canvas.drawLine((float) a_screen.x, (float) a_screen.y,
|
||||
(float) b_screen.x, (float) b_screen.y,
|
||||
paint);
|
||||
}
|
||||
}
|
||||
|
||||
void set_a(AltosLatLon a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
void set_b(AltosLatLon b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
Line() {
|
||||
}
|
||||
}
|
||||
|
||||
Line line = new Line();
|
||||
|
||||
int stroke_width = 20;
|
||||
|
||||
void draw_text(AltosLatLon lat_lon, String text, int off_x, int off_y) {
|
||||
if (lat_lon != null && map != null && map.transform != null) {
|
||||
AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
|
||||
|
||||
Rect bounds = new Rect();
|
||||
paint.getTextBounds(text, 0, text.length(), bounds);
|
||||
|
||||
int width = bounds.right - bounds.left;
|
||||
int height = bounds.bottom - bounds.top;
|
||||
|
||||
float x = pt.x;
|
||||
float y = pt.y;
|
||||
x = x - width / 2.0f - off_x;
|
||||
y = y + height / 2.0f - off_y;
|
||||
paint.setColor(0xff000000);
|
||||
canvas.drawText(text, 0, text.length(), x, y, paint);
|
||||
}
|
||||
}
|
||||
|
||||
HashMap<Integer,Rocket> rockets = new HashMap<Integer,Rocket>();
|
||||
|
||||
void draw_bitmap(AltosLatLon lat_lon, Bitmap bitmap, int off_x, int off_y) {
|
||||
if (lat_lon != null && map != null && map.transform != null) {
|
||||
AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
|
||||
|
||||
canvas.drawBitmap(bitmap, pt.x - off_x, pt.y - off_y, paint);
|
||||
}
|
||||
}
|
||||
|
||||
private Rocket[] sorted_rockets() {
|
||||
Rocket[] rocket_array = rockets.values().toArray(new Rocket[0]);
|
||||
|
||||
Arrays.sort(rocket_array);
|
||||
return rocket_array;
|
||||
}
|
||||
|
||||
private void draw_positions() {
|
||||
line.set_a(there);
|
||||
line.set_b(here);
|
||||
line.paint();
|
||||
draw_bitmap(pad, pad_bitmap, pad_off_x, pad_off_y);
|
||||
|
||||
for (Rocket rocket : sorted_rockets())
|
||||
rocket.paint();
|
||||
draw_bitmap(here, here_bitmap, here_off_x, here_off_y);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas view_canvas) {
|
||||
if (map == null) {
|
||||
debug("MapView draw without map\n");
|
||||
return;
|
||||
}
|
||||
if (map.transform == null) {
|
||||
debug("MapView draw without transform\n");
|
||||
return;
|
||||
}
|
||||
canvas = view_canvas;
|
||||
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
paint.setStrokeWidth(stroke_width);
|
||||
paint.setStrokeCap(Paint.Cap.ROUND);
|
||||
paint.setStrokeJoin(Paint.Join.ROUND);
|
||||
paint.setTextSize(40);
|
||||
map.paint();
|
||||
draw_positions();
|
||||
canvas = null;
|
||||
}
|
||||
|
||||
public boolean onScale(ScaleGestureDetector detector) {
|
||||
float f = detector.getScaleFactor();
|
||||
|
||||
if (f <= 0.8) {
|
||||
map.set_zoom_centre(map.get_zoom() - 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY()));
|
||||
return true;
|
||||
}
|
||||
if (f >= 1.2) {
|
||||
map.set_zoom_centre(map.get_zoom() + 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onScaleBegin(ScaleGestureDetector detector) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent event) {
|
||||
scale_detector.onTouchEvent(event);
|
||||
|
||||
if (scale_detector.isInProgress()) {
|
||||
scaling = true;
|
||||
}
|
||||
|
||||
if (scaling) {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
scaling = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
map.touch_start((int) event.getX(), (int) event.getY(), true);
|
||||
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
|
||||
map.touch_continue((int) event.getX(), (int) event.getY(), true);
|
||||
} else if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
map.touch_stop((int) event.getX(), (int) event.getY(), true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
double mapAccuracy;
|
||||
|
||||
public void center(double lat, double lon, double accuracy) {
|
||||
if (mapAccuracy <= 0 || accuracy < mapAccuracy/10 || (map != null && !map.has_centre())) {
|
||||
if (map != null)
|
||||
map.maybe_centre(lat, lon);
|
||||
mapAccuracy = accuracy;
|
||||
}
|
||||
}
|
||||
|
||||
public void set_visible(boolean visible) {
|
||||
if (visible)
|
||||
setVisibility(VISIBLE);
|
||||
else
|
||||
setVisibility(GONE);
|
||||
}
|
||||
|
||||
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
|
||||
boolean changed = false;
|
||||
|
||||
if (state != null) {
|
||||
map.show(state, null);
|
||||
if (state.pad_lat != AltosLib.MISSING && pad == null)
|
||||
pad = new AltosLatLon(state.pad_lat, state.pad_lon);
|
||||
}
|
||||
|
||||
if (telem_state != null) {
|
||||
Integer[] old_serial = rockets.keySet().toArray(new Integer[0]);
|
||||
Integer[] new_serial = telem_state.keySet().toArray(new Integer[0]);
|
||||
|
||||
/* remove deleted keys */
|
||||
for (int serial : old_serial) {
|
||||
if (!telem_state.containsKey(serial))
|
||||
rockets.remove(serial);
|
||||
}
|
||||
|
||||
/* set remaining keys */
|
||||
|
||||
for (int serial : new_serial) {
|
||||
Rocket rocket;
|
||||
AltosState t_state = telem_state.get(serial);
|
||||
if (rockets.containsKey(serial))
|
||||
rocket = rockets.get(serial);
|
||||
else {
|
||||
rocket = new Rocket(serial, this);
|
||||
rockets.put(serial, rocket);
|
||||
}
|
||||
if (t_state.gps != null) {
|
||||
AltosLatLon latlon = new AltosLatLon(t_state.gps.lat, t_state.gps.lon);
|
||||
rocket.set_position(latlon, t_state.received_time);
|
||||
if (state != null && state.cal_data().serial == serial)
|
||||
there = latlon;
|
||||
}
|
||||
if (state != null)
|
||||
rocket.set_active(state.cal_data().serial == serial);
|
||||
}
|
||||
}
|
||||
if (receiver != null) {
|
||||
AltosLatLon new_here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
|
||||
if (!new_here.equals(here)) {
|
||||
here = new_here;
|
||||
AltosDebug.debug("Location changed, redraw");
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onCreateView(AltosDroid altos_droid) {
|
||||
this.altos_droid = altos_droid;
|
||||
map = new AltosMap(this, scale);
|
||||
AltosPreferences.register_map_type_listener(this);
|
||||
map.set_maptype(AltosPreferences.map_type());
|
||||
|
||||
pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad);
|
||||
/* arrow at the bottom of the launchpad image */
|
||||
pad_off_x = pad_bitmap.getWidth() / 2;
|
||||
pad_off_y = pad_bitmap.getHeight();
|
||||
|
||||
rocket_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket);
|
||||
/* arrow at the bottom of the rocket image */
|
||||
rocket_off_x = rocket_bitmap.getWidth() / 2;
|
||||
rocket_off_y = rocket_bitmap.getHeight();
|
||||
|
||||
here_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_maps_indicator_current_position);
|
||||
/* Center of the dot */
|
||||
here_off_x = here_bitmap.getWidth() / 2;
|
||||
here_off_y = here_bitmap.getHeight() / 2;
|
||||
}
|
||||
|
||||
public void onDestroyView() {
|
||||
AltosPreferences.unregister_map_type_listener(this);
|
||||
}
|
||||
|
||||
public void map_type_changed(int map_type) {
|
||||
if (map != null)
|
||||
map.set_maptype(map_type);
|
||||
}
|
||||
|
||||
public AltosMapOffline(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.altos_droid = altos_droid;
|
||||
scale_detector = new ScaleGestureDetector(context, this);
|
||||
}
|
||||
}
|
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.altoslib_14.*;
|
||||
|
||||
import com.google.android.gms.maps.*;
|
||||
import com.google.android.gms.maps.model.*;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.*;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.location.Location;
|
||||
import android.content.*;
|
||||
|
||||
class RocketOnline implements Comparable {
|
||||
Marker marker;
|
||||
int serial;
|
||||
long last_packet;
|
||||
int size;
|
||||
|
||||
void set_position(AltosLatLon position, long last_packet) {
|
||||
marker.setPosition(new LatLng(position.lat, position.lon));
|
||||
this.last_packet = last_packet;
|
||||
}
|
||||
|
||||
private Bitmap rocket_bitmap(Context context, String text) {
|
||||
|
||||
/* From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
|
||||
*/
|
||||
Bitmap orig_bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.rocket);
|
||||
Bitmap bitmap = orig_bitmap.copy(Bitmap.Config.ARGB_8888, true);
|
||||
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
Paint paint = new Paint();
|
||||
paint.setTextSize(40);
|
||||
paint.setColor(0xff000000);
|
||||
|
||||
Rect bounds = new Rect();
|
||||
paint.getTextBounds(text, 0, text.length(), bounds);
|
||||
|
||||
int width = bounds.right - bounds.left;
|
||||
int height = bounds.bottom - bounds.top;
|
||||
|
||||
float x = bitmap.getWidth() / 2.0f - width / 2.0f;
|
||||
float y = bitmap.getHeight() / 2.0f - height / 2.0f;
|
||||
|
||||
size = bitmap.getWidth();
|
||||
|
||||
canvas.drawText(text, 0, text.length(), x, y, paint);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
marker.remove();
|
||||
}
|
||||
|
||||
public int compareTo(Object o) {
|
||||
RocketOnline other = (RocketOnline) o;
|
||||
|
||||
long diff = last_packet - other.last_packet;
|
||||
|
||||
if (diff > 0)
|
||||
return 1;
|
||||
if (diff < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
RocketOnline(Context context, int serial, GoogleMap map, double lat, double lon, long last_packet) {
|
||||
this.serial = serial;
|
||||
String name = String.format("%d", serial);
|
||||
this.marker = map.addMarker(new MarkerOptions()
|
||||
.icon(BitmapDescriptorFactory.fromBitmap(rocket_bitmap(context, name)))
|
||||
.position(new LatLng(lat, lon))
|
||||
.visible(true));
|
||||
this.last_packet = last_packet;
|
||||
}
|
||||
}
|
||||
|
||||
public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarkerClickListener, GoogleMap.OnMapClickListener, OnMapReadyCallback, AltosMapTypeListener {
|
||||
public AltosOnlineMapFragment mMapFragment;
|
||||
private GoogleMap mMap;
|
||||
private boolean mapLoaded = false;
|
||||
Context context;
|
||||
|
||||
private HashMap<Integer,RocketOnline> rockets = new HashMap<Integer,RocketOnline>();
|
||||
private Marker mPadMarker;
|
||||
private boolean pad_set;
|
||||
private Polyline mPolyline;
|
||||
|
||||
private double mapAccuracy = -1;
|
||||
|
||||
private AltosLatLon my_position = null;
|
||||
private AltosLatLon target_position = null;
|
||||
|
||||
private AltosDroid altos_droid;
|
||||
|
||||
public static class AltosOnlineMapFragment extends SupportMapFragment {
|
||||
AltosMapOnline c;
|
||||
View map_view;
|
||||
|
||||
public AltosOnlineMapFragment(AltosMapOnline c) {
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
public AltosOnlineMapFragment() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
if (c != null)
|
||||
getMapAsync(c);
|
||||
}
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
map_view = super.onCreateView(inflater, container, savedInstanceState);
|
||||
return map_view;
|
||||
}
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
map_view = null;
|
||||
}
|
||||
public void set_visible(boolean visible) {
|
||||
if (map_view == null)
|
||||
return;
|
||||
if (visible)
|
||||
map_view.setVisibility(View.VISIBLE);
|
||||
else
|
||||
map_view.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void onCreateView(AltosDroid altos_droid) {
|
||||
this.altos_droid = altos_droid;
|
||||
AltosPreferences.register_map_type_listener(this);
|
||||
mMapFragment = new AltosOnlineMapFragment(this);
|
||||
}
|
||||
|
||||
public void onDestroyView() {
|
||||
AltosPreferences.unregister_map_type_listener(this);
|
||||
}
|
||||
|
||||
private double pixel_distance(LatLng a, LatLng b) {
|
||||
Projection projection = mMap.getProjection();
|
||||
|
||||
Point a_pt = projection.toScreenLocation(a);
|
||||
Point b_pt = projection.toScreenLocation(b);
|
||||
|
||||
return Math.hypot((double) (a_pt.x - b_pt.x), (double) (a_pt.y - b_pt.y));
|
||||
}
|
||||
|
||||
private RocketOnline[] sorted_rockets() {
|
||||
synchronized(rockets) {
|
||||
RocketOnline[] rocket_array = rockets.values().toArray(new RocketOnline[0]);
|
||||
|
||||
Arrays.sort(rocket_array);
|
||||
return rocket_array;
|
||||
}
|
||||
}
|
||||
|
||||
public void onMapClick(LatLng lat_lng) {
|
||||
ArrayList<Integer> near = new ArrayList<Integer>();
|
||||
|
||||
for (RocketOnline rocket : sorted_rockets()) {
|
||||
LatLng pos = rocket.marker.getPosition();
|
||||
|
||||
if (pos == null)
|
||||
continue;
|
||||
|
||||
double distance = pixel_distance(lat_lng, pos);
|
||||
if (distance < rocket.size * 2)
|
||||
near.add(rocket.serial);
|
||||
}
|
||||
|
||||
if (near.size() != 0)
|
||||
altos_droid.touch_trackers(near.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
public boolean onMarkerClick(Marker marker) {
|
||||
onMapClick(marker.getPosition());
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
position_permission() {
|
||||
if (mMap != null)
|
||||
mMap.setMyLocationEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapReady(GoogleMap googleMap) {
|
||||
final int map_type = AltosPreferences.map_type();
|
||||
mMap = googleMap;
|
||||
if (mMap != null) {
|
||||
map_type_changed(map_type);
|
||||
if (altos_droid.have_location_permission)
|
||||
mMap.setMyLocationEnabled(true);
|
||||
else
|
||||
altos_droid.tell_map_permission(this);
|
||||
mMap.getUiSettings().setTiltGesturesEnabled(false);
|
||||
mMap.getUiSettings().setZoomControlsEnabled(false);
|
||||
mMap.setOnMarkerClickListener(this);
|
||||
mMap.setOnMapClickListener(this);
|
||||
|
||||
mPadMarker = mMap.addMarker(
|
||||
new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad))
|
||||
.position(new LatLng(0,0))
|
||||
.visible(false)
|
||||
);
|
||||
|
||||
mPolyline = mMap.addPolyline(
|
||||
new PolylineOptions().add(new LatLng(0,0), new LatLng(0,0))
|
||||
.width(20)
|
||||
.color(Color.BLUE)
|
||||
.visible(false)
|
||||
);
|
||||
|
||||
mapLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void center(double lat, double lon, double accuracy) {
|
||||
if (mMap == null)
|
||||
return;
|
||||
|
||||
if (mapAccuracy < 0 || accuracy < mapAccuracy/10) {
|
||||
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lon),14));
|
||||
mapAccuracy = accuracy;
|
||||
}
|
||||
}
|
||||
|
||||
private void set_rocket(int serial, AltosState state) {
|
||||
RocketOnline rocket;
|
||||
|
||||
if (state.gps == null || state.gps.lat == AltosLib.MISSING)
|
||||
return;
|
||||
|
||||
if (mMap == null)
|
||||
return;
|
||||
|
||||
synchronized(rockets) {
|
||||
if (rockets.containsKey(serial)) {
|
||||
rocket = rockets.get(serial);
|
||||
rocket.set_position(new AltosLatLon(state.gps.lat, state.gps.lon), state.received_time);
|
||||
} else {
|
||||
rocket = new RocketOnline(context,
|
||||
serial,
|
||||
mMap, state.gps.lat, state.gps.lon,
|
||||
state.received_time);
|
||||
rockets.put(serial, rocket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void remove_rocket(int serial) {
|
||||
synchronized(rockets) {
|
||||
RocketOnline rocket = rockets.get(serial);
|
||||
rocket.remove();
|
||||
rockets.remove(serial);
|
||||
}
|
||||
}
|
||||
|
||||
public void set_visible(boolean visible) {
|
||||
if (mMapFragment != null)
|
||||
mMapFragment.set_visible(visible);
|
||||
}
|
||||
|
||||
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
|
||||
|
||||
if (telem_state != null) {
|
||||
synchronized(rockets) {
|
||||
for (int serial : rockets.keySet()) {
|
||||
if (!telem_state.containsKey(serial))
|
||||
remove_rocket(serial);
|
||||
}
|
||||
|
||||
for (int serial : telem_state.keySet()) {
|
||||
set_rocket(serial, telem_state.get(serial));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state != null) {
|
||||
if (mapLoaded) {
|
||||
if (!pad_set && state.pad_lat != AltosLib.MISSING) {
|
||||
pad_set = true;
|
||||
mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon));
|
||||
mPadMarker.setVisible(true);
|
||||
}
|
||||
}
|
||||
if (state.gps != null && state.gps.lat != AltosLib.MISSING) {
|
||||
|
||||
target_position = new AltosLatLon(state.gps.lat, state.gps.lon);
|
||||
if (state.gps.locked && state.gps.nsat >= 4)
|
||||
center (state.gps.lat, state.gps.lon, 10);
|
||||
}
|
||||
}
|
||||
|
||||
if (receiver != null) {
|
||||
double accuracy;
|
||||
|
||||
if (receiver.hasAccuracy())
|
||||
accuracy = receiver.getAccuracy();
|
||||
else
|
||||
accuracy = 1000;
|
||||
|
||||
my_position = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
|
||||
center (my_position.lat, my_position.lon, accuracy);
|
||||
}
|
||||
|
||||
if (my_position != null && target_position != null && mPolyline != null) {
|
||||
mPolyline.setPoints(Arrays.asList(new LatLng(my_position.lat, my_position.lon), new LatLng(target_position.lat, target_position.lon)));
|
||||
mPolyline.setVisible(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void map_type_changed(int map_type) {
|
||||
if (mMap != null) {
|
||||
if (map_type == AltosMap.maptype_hybrid)
|
||||
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
|
||||
else if (map_type == AltosMap.maptype_satellite)
|
||||
mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
|
||||
else if (map_type == AltosMap.maptype_terrain)
|
||||
mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
|
||||
else
|
||||
mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
public AltosMapOnline(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
}
|
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright © 2015 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.usb.*;
|
||||
import android.app.*;
|
||||
import android.os.Handler;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosUsb extends AltosDroidLink {
|
||||
|
||||
private Thread input_thread = null;
|
||||
|
||||
private Handler handler;
|
||||
|
||||
private UsbManager manager;
|
||||
private UsbDevice device;
|
||||
private UsbDeviceConnection connection;
|
||||
private UsbInterface iface;
|
||||
private UsbEndpoint in, out;
|
||||
|
||||
private InputStream input;
|
||||
private OutputStream output;
|
||||
|
||||
// Constructor
|
||||
public AltosUsb(Context context, UsbDevice device, Handler handler) {
|
||||
super(handler);
|
||||
// set_debug(D);
|
||||
this.handler = handler;
|
||||
|
||||
iface = null;
|
||||
in = null;
|
||||
out = null;
|
||||
|
||||
int niface = device.getInterfaceCount();
|
||||
|
||||
for (int i = 0; i < niface; i++) {
|
||||
|
||||
iface = device.getInterface(i);
|
||||
|
||||
in = null;
|
||||
out = null;
|
||||
|
||||
int nendpoints = iface.getEndpointCount();
|
||||
|
||||
for (int e = 0; e < nendpoints; e++) {
|
||||
UsbEndpoint endpoint = iface.getEndpoint(e);
|
||||
|
||||
if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
|
||||
switch (endpoint.getDirection()) {
|
||||
case UsbConstants.USB_DIR_OUT:
|
||||
out = endpoint;
|
||||
break;
|
||||
case UsbConstants.USB_DIR_IN:
|
||||
in = endpoint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (in != null && out != null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (in != null && out != null) {
|
||||
AltosDebug.debug("\tin %s out %s\n", in.toString(), out.toString());
|
||||
|
||||
manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||||
|
||||
if (manager == null) {
|
||||
AltosDebug.debug("USB_SERVICE failed");
|
||||
return;
|
||||
}
|
||||
|
||||
connection = manager.openDevice(device);
|
||||
|
||||
if (connection == null) {
|
||||
AltosDebug.debug("openDevice failed");
|
||||
return;
|
||||
}
|
||||
|
||||
connection.claimInterface(iface, true);
|
||||
|
||||
input_thread = new Thread(this);
|
||||
input_thread.start();
|
||||
|
||||
// Configure the newly connected device for telemetry
|
||||
print("~\nE 0\n");
|
||||
set_monitor(false);
|
||||
}
|
||||
}
|
||||
|
||||
static private boolean isAltusMetrum(UsbDevice device) {
|
||||
if (device.getVendorId() != AltosLib.vendor_altusmetrum)
|
||||
return false;
|
||||
if (device.getProductId() < AltosLib.product_altusmetrum_min)
|
||||
return false;
|
||||
if (device.getProductId() > AltosLib.product_altusmetrum_max)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean matchProduct(int want_product, UsbDevice device) {
|
||||
|
||||
if (!isAltusMetrum(device))
|
||||
return false;
|
||||
|
||||
if (want_product == AltosLib.product_any)
|
||||
return true;
|
||||
|
||||
int have_product = device.getProductId();
|
||||
|
||||
if (want_product == AltosLib.product_basestation)
|
||||
return have_product == AltosLib.product_teledongle ||
|
||||
have_product == AltosLib.product_telebt ||
|
||||
have_product == AltosLib.product_megadongle;
|
||||
|
||||
if (want_product == AltosLib.product_altimeter)
|
||||
return have_product == AltosLib.product_telemetrum ||
|
||||
have_product == AltosLib.product_telemega ||
|
||||
have_product == AltosLib.product_easymega ||
|
||||
have_product == AltosLib.product_telegps ||
|
||||
have_product == AltosLib.product_easymini ||
|
||||
have_product == AltosLib.product_telemini ||
|
||||
have_product == AltosLib.product_easytimer;
|
||||
|
||||
if (have_product == AltosLib.product_altusmetrum) /* old devices match any request */
|
||||
return true;
|
||||
|
||||
if (want_product == have_product)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static public boolean request_permission(Context context, UsbDevice device, PendingIntent pi) {
|
||||
UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||||
|
||||
// if (manager.hasPermission(device))
|
||||
// return true;
|
||||
|
||||
AltosDebug.debug("request permission for USB device " + device.toString());
|
||||
|
||||
manager.requestPermission(device, pi);
|
||||
return false;
|
||||
}
|
||||
|
||||
static public UsbDevice find_device(Context context, int match_product) {
|
||||
UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||||
|
||||
HashMap<String,UsbDevice> devices = manager.getDeviceList();
|
||||
|
||||
for (UsbDevice device : devices.values()) {
|
||||
int vendor = device.getVendorId();
|
||||
int product = device.getProductId();
|
||||
|
||||
if (matchProduct(match_product, device)) {
|
||||
AltosDebug.debug("found USB device " + device.toString());
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void disconnected() {
|
||||
if (closed()) {
|
||||
AltosDebug.debug("disconnected after closed");
|
||||
return;
|
||||
}
|
||||
|
||||
AltosDebug.debug("Sending disconnected message");
|
||||
handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
|
||||
}
|
||||
|
||||
void close_device() {
|
||||
UsbDeviceConnection tmp_connection;
|
||||
|
||||
synchronized(this) {
|
||||
tmp_connection = connection;
|
||||
connection = null;
|
||||
}
|
||||
|
||||
if (tmp_connection != null) {
|
||||
AltosDebug.debug("Closing USB device");
|
||||
tmp_connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
int read(byte[] buffer, int len) {
|
||||
if (connection == null)
|
||||
return 0;
|
||||
int ret = connection.bulkTransfer(in, buffer, len, -1);
|
||||
AltosDebug.debug("read(%d) = %d\n", len, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int write(byte[] buffer, int len) {
|
||||
if (connection == null)
|
||||
return 0;
|
||||
int ret = connection.bulkTransfer(out, buffer, len, -1);
|
||||
AltosDebug.debug("write(%d) = %d\n", len, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Stubs of required methods when extending AltosLink
|
||||
public boolean can_cancel_reply() { return false; }
|
||||
public boolean show_reply_timeout() { return true; }
|
||||
public void hide_reply_timeout() { }
|
||||
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 android.content.Context;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
public class AltosViewPager extends ViewPager {
|
||||
|
||||
public AltosViewPager(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public AltosViewPager(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
|
||||
|
||||
if (v.getClass() != null &&
|
||||
v.getClass().getName() != null &&
|
||||
v.getClass().getName().endsWith("MapOffline"))
|
||||
return true;
|
||||
|
||||
if(v.getClass() != null &&
|
||||
v.getClass().getPackage() != null &&
|
||||
v.getClass().getPackage().getName() != null &&
|
||||
v.getClass().getPackage().getName().startsWith("maps."))
|
||||
return true;
|
||||
|
||||
return super.canScroll(v, checkV, dx, x, y);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Copyright © 2011 Keith Packard <keithp@keithp.com>
|
||||
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 android.speech.tts.TextToSpeech;
|
||||
import android.speech.tts.TextToSpeech.OnInitListener;
|
||||
import android.location.Location;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosVoice {
|
||||
|
||||
private TextToSpeech tts = null;
|
||||
private boolean tts_enabled = false;
|
||||
|
||||
static final int TELL_MODE_NONE = 0;
|
||||
static final int TELL_MODE_PAD = 1;
|
||||
static final int TELL_MODE_FLIGHT = 2;
|
||||
static final int TELL_MODE_RECOVER = 3;
|
||||
|
||||
static final int TELL_FLIGHT_NONE = 0;
|
||||
static final int TELL_FLIGHT_STATE = 1;
|
||||
static final int TELL_FLIGHT_SPEED = 2;
|
||||
static final int TELL_FLIGHT_HEIGHT = 3;
|
||||
static final int TELL_FLIGHT_TRACK = 4;
|
||||
|
||||
private int last_tell_mode;
|
||||
private int last_tell_serial = AltosLib.MISSING;
|
||||
private int last_state;
|
||||
private AltosGPS last_gps;
|
||||
private double last_height = AltosLib.MISSING;
|
||||
private Location last_receiver;
|
||||
private long last_speak_time;
|
||||
private int last_flight_tell = TELL_FLIGHT_NONE;
|
||||
private boolean quiet = false;
|
||||
|
||||
private long now() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private void reset_last() {
|
||||
last_tell_mode = TELL_MODE_NONE;
|
||||
last_speak_time = now() - 100 * 1000;
|
||||
last_gps = null;
|
||||
last_height = AltosLib.MISSING;
|
||||
last_receiver = null;
|
||||
last_state = AltosLib.ao_flight_invalid;
|
||||
last_flight_tell = TELL_FLIGHT_NONE;
|
||||
}
|
||||
|
||||
public AltosVoice(AltosDroid a) {
|
||||
tts = new TextToSpeech(a, new OnInitListener() {
|
||||
public void onInit(int status) {
|
||||
if (status == TextToSpeech.SUCCESS) tts_enabled = true;
|
||||
}
|
||||
});
|
||||
reset_last();
|
||||
}
|
||||
|
||||
public synchronized void set_enable(boolean enable) {
|
||||
tts_enabled = enable;
|
||||
}
|
||||
|
||||
public synchronized void speak(String s) {
|
||||
if (!tts_enabled) return;
|
||||
last_speak_time = now();
|
||||
if (!quiet)
|
||||
tts.speak(s, TextToSpeech.QUEUE_ADD, null, null);
|
||||
}
|
||||
|
||||
public synchronized long time_since_speak() {
|
||||
return now() - last_speak_time;
|
||||
}
|
||||
|
||||
public synchronized void speak(String format, Object ... arguments) {
|
||||
speak(String.format(format, arguments));
|
||||
}
|
||||
|
||||
public synchronized boolean is_speaking() {
|
||||
return tts.isSpeaking();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (tts != null) {
|
||||
tts.stop();
|
||||
tts.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean last_apogee_good;
|
||||
private boolean last_main_good;
|
||||
private boolean last_gps_good;
|
||||
|
||||
private boolean tell_gonogo(String name,
|
||||
boolean current,
|
||||
boolean previous,
|
||||
boolean new_mode) {
|
||||
if (current != previous || new_mode)
|
||||
speak("%s %s.", name, current ? "ready" : "not ready");
|
||||
return current;
|
||||
}
|
||||
|
||||
private boolean tell_pad(TelemetryState telem_state, AltosState state,
|
||||
AltosGreatCircle from_receiver, Location receiver) {
|
||||
|
||||
if (state == null)
|
||||
return false;
|
||||
|
||||
if (state.apogee_voltage != AltosLib.MISSING)
|
||||
last_apogee_good = tell_gonogo("apogee",
|
||||
state.apogee_voltage >= AltosLib.ao_igniter_good,
|
||||
last_apogee_good,
|
||||
last_tell_mode != TELL_MODE_PAD);
|
||||
|
||||
if (state.main_voltage != AltosLib.MISSING)
|
||||
last_main_good = tell_gonogo("main",
|
||||
state.main_voltage >= AltosLib.ao_igniter_good,
|
||||
last_main_good,
|
||||
last_tell_mode != TELL_MODE_PAD);
|
||||
|
||||
if (state.gps != null)
|
||||
last_gps_good = tell_gonogo("G P S",
|
||||
state.gps_ready,
|
||||
last_gps_good,
|
||||
last_tell_mode != TELL_MODE_PAD);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private boolean descending(int state) {
|
||||
return AltosLib.ao_flight_drogue <= state && state <= AltosLib.ao_flight_landed;
|
||||
}
|
||||
|
||||
private boolean target_moved(AltosState state) {
|
||||
if (last_gps != null && state != null && state.gps != null) {
|
||||
AltosGreatCircle moved = new AltosGreatCircle(last_gps.lat, last_gps.lon, last_gps.alt,
|
||||
state.gps.lat, state.gps.lon, state.gps.alt);
|
||||
double height_change = 0;
|
||||
double height = state.height();
|
||||
|
||||
if (height != AltosLib.MISSING && last_height != AltosLib.MISSING)
|
||||
height_change = Math.abs(last_height - height);
|
||||
|
||||
if (moved.range < 10 && height_change < 10)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean receiver_moved(Location receiver) {
|
||||
if (last_receiver != null && receiver != null) {
|
||||
AltosGreatCircle moved = new AltosGreatCircle(last_receiver.getLatitude(),
|
||||
last_receiver.getLongitude(),
|
||||
last_receiver.getAltitude(),
|
||||
receiver.getLatitude(),
|
||||
receiver.getLongitude(),
|
||||
receiver.getAltitude());
|
||||
if (moved.range < 10)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean tell_flight(TelemetryState telem_state, AltosState state,
|
||||
AltosGreatCircle from_receiver, Location receiver) {
|
||||
|
||||
boolean spoken = false;
|
||||
|
||||
if (state == null)
|
||||
return false;
|
||||
|
||||
if (last_tell_mode != TELL_MODE_FLIGHT)
|
||||
last_flight_tell = TELL_FLIGHT_NONE;
|
||||
|
||||
if (state.state() != last_state && AltosLib.ao_flight_boost <= state.state() && state.state() <= AltosLib.ao_flight_landed) {
|
||||
speak(state.state_name());
|
||||
if (descending(state.state()) && !descending(last_state)) {
|
||||
if (state.max_height() != AltosLib.MISSING) {
|
||||
speak("max height: %s.",
|
||||
AltosConvert.height.say_units(state.max_height()));
|
||||
}
|
||||
}
|
||||
last_flight_tell = TELL_FLIGHT_STATE;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (last_tell_mode == TELL_MODE_FLIGHT && last_flight_tell == TELL_FLIGHT_TRACK) {
|
||||
if (time_since_speak() < 10 * 1000)
|
||||
return false;
|
||||
if (!target_moved(state) && !receiver_moved(receiver))
|
||||
return false;
|
||||
}
|
||||
|
||||
double speed;
|
||||
double height;
|
||||
|
||||
if (last_flight_tell == TELL_FLIGHT_NONE || last_flight_tell == TELL_FLIGHT_STATE || last_flight_tell == TELL_FLIGHT_TRACK) {
|
||||
last_flight_tell = TELL_FLIGHT_SPEED;
|
||||
|
||||
if (state.state() <= AltosLib.ao_flight_coast) {
|
||||
speed = state.speed();
|
||||
} else {
|
||||
speed = state.gps_speed();
|
||||
if (speed == AltosLib.MISSING)
|
||||
speed = state.speed();
|
||||
}
|
||||
|
||||
if (speed != AltosLib.MISSING) {
|
||||
speak("speed: %s.", AltosConvert.speed.say_units(speed));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_flight_tell == TELL_FLIGHT_SPEED) {
|
||||
last_flight_tell = TELL_FLIGHT_HEIGHT;
|
||||
height = state.height();
|
||||
|
||||
if (height != AltosLib.MISSING) {
|
||||
speak("height: %s.", AltosConvert.height.say_units(height));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_flight_tell == TELL_FLIGHT_HEIGHT) {
|
||||
last_flight_tell = TELL_FLIGHT_TRACK;
|
||||
if (from_receiver != null) {
|
||||
speak("bearing %s %d, elevation %d, distance %s.",
|
||||
from_receiver.bearing_words(
|
||||
AltosGreatCircle.BEARING_VOICE),
|
||||
(int) (from_receiver.bearing + 0.5),
|
||||
(int) (from_receiver.elevation + 0.5),
|
||||
AltosConvert.distance.say(from_receiver.distance));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return spoken;
|
||||
}
|
||||
|
||||
private boolean tell_recover(TelemetryState telem_state, AltosState state,
|
||||
AltosGreatCircle from_receiver, Location receiver) {
|
||||
|
||||
if (from_receiver == null)
|
||||
return false;
|
||||
|
||||
if (last_tell_mode == TELL_MODE_RECOVER) {
|
||||
if (!target_moved(state) && !receiver_moved(receiver))
|
||||
return false;
|
||||
if (time_since_speak() <= 10 * 1000)
|
||||
return false;
|
||||
}
|
||||
|
||||
String direction = AltosDroid.direction(from_receiver, receiver);
|
||||
if (direction == null)
|
||||
direction = String.format("Bearing %d", (int) (from_receiver.bearing + 0.5));
|
||||
|
||||
speak("%s, distance %s.", direction,
|
||||
AltosConvert.distance.say_units(from_receiver.distance));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void tell(TelemetryState telem_state, AltosState state,
|
||||
AltosGreatCircle from_receiver, Location receiver,
|
||||
AltosDroidTab tab, boolean quiet) {
|
||||
|
||||
this.quiet = quiet;
|
||||
|
||||
boolean spoken = false;
|
||||
|
||||
if (!tts_enabled) return;
|
||||
|
||||
if (is_speaking()) return;
|
||||
|
||||
int tell_serial = last_tell_serial;
|
||||
|
||||
if (state != null)
|
||||
tell_serial = state.cal_data().serial;
|
||||
|
||||
if (tell_serial != last_tell_serial)
|
||||
reset_last();
|
||||
|
||||
int tell_mode = TELL_MODE_NONE;
|
||||
|
||||
if (tab.tab_name().equals(AltosDroid.tab_pad_name))
|
||||
tell_mode = TELL_MODE_PAD;
|
||||
else if (tab.tab_name().equals(AltosDroid.tab_flight_name))
|
||||
tell_mode = TELL_MODE_FLIGHT;
|
||||
else
|
||||
tell_mode = TELL_MODE_RECOVER;
|
||||
|
||||
if (tell_mode == TELL_MODE_PAD)
|
||||
spoken = tell_pad(telem_state, state, from_receiver, receiver);
|
||||
else if (tell_mode == TELL_MODE_FLIGHT)
|
||||
spoken = tell_flight(telem_state, state, from_receiver, receiver);
|
||||
else
|
||||
spoken = tell_recover(telem_state, state, from_receiver, receiver);
|
||||
|
||||
if (spoken) {
|
||||
last_tell_mode = tell_mode;
|
||||
last_tell_serial = tell_serial;
|
||||
if (state != null) {
|
||||
last_state = state.state();
|
||||
last_height = state.height();
|
||||
if (state.gps != null)
|
||||
last_gps = state.gps;
|
||||
}
|
||||
if (receiver != null)
|
||||
last_receiver = receiver;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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;
|
||||
|
||||
public class BuildInfo {
|
||||
public static final String version = "@VERSION@";
|
||||
public static final String git_describe = "@DESCRIBE@";
|
||||
public static final String branch = "@BRANCH@";
|
||||
public static final String commitnum = "@COMMITNUM@";
|
||||
public static final String commithash = "@COMMITHASH@";
|
||||
public static final String builddate = "@BUILDDATE@";
|
||||
public static final String buildtime = "@BUILDTIME@";
|
||||
public static final String buildtz = "@BUILDTZ@";
|
||||
}
|
||||
|
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright © 2015 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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;
|
||||
|
||||
public class DeviceAddress {
|
||||
public String address;
|
||||
public String name;
|
||||
|
||||
public DeviceAddress(String address, String name) {
|
||||
this.address = address;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.AltosDroid;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
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.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
|
||||
/**
|
||||
* This Activity appears as a dialog. It lists any paired devices and
|
||||
* devices detected in the area after discovery. When a device is chosen
|
||||
* by the user, the MAC address of the device is sent back to the parent
|
||||
* Activity in the result Intent.
|
||||
*/
|
||||
public class DeviceListActivity extends Activity {
|
||||
|
||||
// Return Intent extra
|
||||
public static final String EXTRA_DEVICE_ADDRESS = "device_address";
|
||||
public static final String EXTRA_DEVICE_NAME = "device_name";
|
||||
|
||||
// Member fields
|
||||
private BluetoothAdapter mBtAdapter;
|
||||
private ArrayAdapter<String> mPairedDevicesArrayAdapter;
|
||||
private ArrayAdapter<String> mNewDevicesArrayAdapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Setup the window
|
||||
setContentView(R.layout.device_list);
|
||||
|
||||
// Set result CANCELED incase the user backs out
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
|
||||
// Initialize the button to perform device discovery
|
||||
Button scanButton = (Button) findViewById(R.id.button_scan);
|
||||
scanButton.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
doDiscovery();
|
||||
v.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize array adapters. One for already paired devices and
|
||||
// one for newly discovered devices
|
||||
mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
|
||||
mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
|
||||
|
||||
// Find and set up the ListView for paired devices
|
||||
ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
|
||||
pairedListView.setAdapter(mPairedDevicesArrayAdapter);
|
||||
pairedListView.setOnItemClickListener(mDeviceClickListener);
|
||||
|
||||
// Find and set up the ListView for newly discovered devices
|
||||
ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
|
||||
newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
|
||||
newDevicesListView.setOnItemClickListener(mDeviceClickListener);
|
||||
|
||||
// Register for broadcasts when a device is discovered
|
||||
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
|
||||
this.registerReceiver(mReceiver, filter);
|
||||
|
||||
// Register for broadcasts when discovery has finished
|
||||
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
|
||||
this.registerReceiver(mReceiver, filter);
|
||||
|
||||
// Get the local Bluetooth adapter
|
||||
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
|
||||
// Get a set of currently paired devices
|
||||
Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
|
||||
|
||||
// If there are paired devices, add each one to the ArrayAdapter
|
||||
if (pairedDevices.size() > 0) {
|
||||
findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
|
||||
for (BluetoothDevice device : pairedDevices) {
|
||||
String name = device.getName();
|
||||
if (name != null && name.startsWith("TeleBT"))
|
||||
mPairedDevicesArrayAdapter.add(name + "\n" + device.getAddress());
|
||||
}
|
||||
} else {
|
||||
String noDevices = getResources().getText(R.string.none_paired).toString();
|
||||
mPairedDevicesArrayAdapter.add(noDevices);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
// Make sure we're not doing discovery anymore
|
||||
if (mBtAdapter != null) {
|
||||
mBtAdapter.cancelDiscovery();
|
||||
}
|
||||
|
||||
// Unregister broadcast listeners
|
||||
this.unregisterReceiver(mReceiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start device discover with the BluetoothAdapter
|
||||
*/
|
||||
private void doDiscovery() {
|
||||
AltosDebug.debug("doDiscovery()");
|
||||
|
||||
// Indicate scanning in the title
|
||||
setTitle(R.string.scanning);
|
||||
|
||||
// Turn on sub-title for new devices
|
||||
findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
|
||||
|
||||
// If we're already discovering, stop it
|
||||
if (mBtAdapter.isDiscovering()) {
|
||||
mBtAdapter.cancelDiscovery();
|
||||
}
|
||||
|
||||
// Request discover from BluetoothAdapter
|
||||
mBtAdapter.startDiscovery();
|
||||
}
|
||||
|
||||
// The on-click listener for all devices in the ListViews
|
||||
private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
|
||||
public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
|
||||
// Get the device MAC address, which is the last 17 chars in the View
|
||||
String info = ((TextView) v).getText().toString();
|
||||
|
||||
/* Ignore clicks on items that are too short */
|
||||
if (info.length() <= 17)
|
||||
return;
|
||||
|
||||
// Cancel discovery because it's costly and we're about to connect
|
||||
mBtAdapter.cancelDiscovery();
|
||||
|
||||
String address = info.substring(info.length() - 17);
|
||||
|
||||
int newline = info.indexOf('\n');
|
||||
|
||||
String name = null;
|
||||
if (newline > 0)
|
||||
name = info.substring(0, newline);
|
||||
else
|
||||
name = info;
|
||||
|
||||
AltosDebug.debug("******* selected item '%s'", info);
|
||||
|
||||
// Create the result Intent and include the MAC address
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(EXTRA_DEVICE_ADDRESS, address);
|
||||
intent.putExtra(EXTRA_DEVICE_NAME, name);
|
||||
|
||||
// Set result and finish this Activity
|
||||
setResult(Activity.RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
};
|
||||
|
||||
// The BroadcastReceiver that listens for discovered devices and
|
||||
// changes the title when discovery is finished
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
|
||||
// When discovery finds a device
|
||||
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
|
||||
|
||||
/* Get the BluetoothDevice object from the Intent
|
||||
*/
|
||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
|
||||
/* If it's already paired, skip it, because it's been listed already
|
||||
*/
|
||||
if (device != null && device.getBondState() != BluetoothDevice.BOND_BONDED)
|
||||
{
|
||||
String name = device.getName();
|
||||
if (name != null && name.startsWith("TeleBT"))
|
||||
mNewDevicesArrayAdapter.add(name + "\n" + device.getAddress());
|
||||
}
|
||||
|
||||
/* When discovery is finished, change the Activity title
|
||||
*/
|
||||
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
|
||||
setTitle(R.string.select_device);
|
||||
if (mNewDevicesArrayAdapter.getCount() == 0) {
|
||||
String noDevices = getResources().getText(R.string.none_found).toString();
|
||||
mNewDevicesArrayAdapter.add(noDevices);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@@ -0,0 +1,183 @@
|
||||
package org.altusmetrum.AltosDroid;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Dumper {
|
||||
private static Dumper instance = new Dumper();
|
||||
|
||||
protected static Dumper getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
class DumpContext {
|
||||
int maxDepth = 0;
|
||||
int maxArrayElements = 0;
|
||||
int callCount = 0;
|
||||
HashMap<String, String> ignoreList = new HashMap<String, String>();
|
||||
HashMap<Object, Integer> visited = new HashMap<Object, Integer>();
|
||||
}
|
||||
|
||||
public static String dump(Object o) {
|
||||
return dump(o, 0, 0, null);
|
||||
}
|
||||
|
||||
public static String dump(Object o, int maxDepth, int maxArrayElements, String[] ignoreList) {
|
||||
DumpContext ctx = Dumper.getInstance().new DumpContext();
|
||||
ctx.maxDepth = maxDepth;
|
||||
ctx.maxArrayElements = maxArrayElements;
|
||||
|
||||
if (ignoreList != null) {
|
||||
for (int i = 0; i < Array.getLength(ignoreList); i++) {
|
||||
int colonIdx = ignoreList[i].indexOf(':');
|
||||
if (colonIdx == -1)
|
||||
ignoreList[i] = ignoreList[i] + ":";
|
||||
ctx.ignoreList.put(ignoreList[i], ignoreList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return dump(o, ctx);
|
||||
}
|
||||
|
||||
protected static String dump(Object o, DumpContext ctx) {
|
||||
if (o == null) {
|
||||
return "<null>";
|
||||
}
|
||||
|
||||
ctx.callCount++;
|
||||
StringBuffer tabs = new StringBuffer();
|
||||
for (int k = 0; k < ctx.callCount; k++) {
|
||||
tabs.append("\t");
|
||||
}
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class oClass = o.getClass();
|
||||
|
||||
String oSimpleName = getSimpleNameWithoutArrayQualifier(oClass);
|
||||
|
||||
if (ctx.ignoreList.get(oSimpleName + ":") != null)
|
||||
return "<Ignored>";
|
||||
|
||||
if (oClass.isArray()) {
|
||||
buffer.append("\n");
|
||||
buffer.append(tabs.toString().substring(1));
|
||||
buffer.append("[\n");
|
||||
int rowCount = ctx.maxArrayElements == 0 ? Array.getLength(o) : Math.min(ctx.maxArrayElements, Array.getLength(o));
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
buffer.append(tabs.toString());
|
||||
try {
|
||||
Object value = Array.get(o, i);
|
||||
buffer.append(dumpValue(value, ctx));
|
||||
} catch (Exception e) {
|
||||
buffer.append(e.getMessage());
|
||||
}
|
||||
if (i < Array.getLength(o) - 1)
|
||||
buffer.append(",");
|
||||
buffer.append("\n");
|
||||
}
|
||||
if (rowCount < Array.getLength(o)) {
|
||||
buffer.append(tabs.toString());
|
||||
buffer.append(Array.getLength(o) - rowCount + " more array elements...");
|
||||
buffer.append("\n");
|
||||
}
|
||||
buffer.append(tabs.toString().substring(1));
|
||||
buffer.append("]");
|
||||
} else {
|
||||
buffer.append("\n");
|
||||
buffer.append(tabs.toString().substring(1));
|
||||
buffer.append("{\n");
|
||||
buffer.append(tabs.toString());
|
||||
buffer.append("hashCode: " + o.hashCode());
|
||||
buffer.append("\n");
|
||||
while (oClass != null && oClass != Object.class) {
|
||||
Field[] fields = oClass.getDeclaredFields();
|
||||
|
||||
if (ctx.ignoreList.get(oClass.getSimpleName()) == null) {
|
||||
if (oClass != o.getClass()) {
|
||||
buffer.append(tabs.toString().substring(1));
|
||||
buffer.append(" Inherited from superclass " + oSimpleName + ":\n");
|
||||
}
|
||||
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
|
||||
String fSimpleName = getSimpleNameWithoutArrayQualifier(fields[i].getType());
|
||||
String fName = fields[i].getName();
|
||||
|
||||
fields[i].setAccessible(true);
|
||||
buffer.append(tabs.toString());
|
||||
buffer.append(fName + "(" + fSimpleName + ")");
|
||||
buffer.append("=");
|
||||
|
||||
if (ctx.ignoreList.get(":" + fName) == null &&
|
||||
ctx.ignoreList.get(fSimpleName + ":" + fName) == null &&
|
||||
ctx.ignoreList.get(fSimpleName + ":") == null) {
|
||||
|
||||
try {
|
||||
Object value = fields[i].get(o);
|
||||
buffer.append(dumpValue(value, ctx));
|
||||
} catch (Exception e) {
|
||||
buffer.append(e.getMessage());
|
||||
}
|
||||
buffer.append("\n");
|
||||
} else {
|
||||
buffer.append("<Ignored>");
|
||||
buffer.append("\n");
|
||||
}
|
||||
}
|
||||
oClass = oClass.getSuperclass();
|
||||
oSimpleName = oClass.getSimpleName();
|
||||
} else {
|
||||
oClass = null;
|
||||
oSimpleName = "";
|
||||
}
|
||||
}
|
||||
buffer.append(tabs.toString().substring(1));
|
||||
buffer.append("}");
|
||||
}
|
||||
ctx.callCount--;
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
protected static String dumpValue(Object value, DumpContext ctx) {
|
||||
if (value == null) {
|
||||
return "<null>";
|
||||
}
|
||||
if (value.getClass().isPrimitive() ||
|
||||
value.getClass() == java.lang.Short.class ||
|
||||
value.getClass() == java.lang.Long.class ||
|
||||
value.getClass() == java.lang.String.class ||
|
||||
value.getClass() == java.lang.Integer.class ||
|
||||
value.getClass() == java.lang.Float.class ||
|
||||
value.getClass() == java.lang.Byte.class ||
|
||||
value.getClass() == java.lang.Character.class ||
|
||||
value.getClass() == java.lang.Double.class ||
|
||||
value.getClass() == java.lang.Boolean.class) {
|
||||
|
||||
return value.toString();
|
||||
|
||||
} else {
|
||||
|
||||
Integer visitedIndex = ctx.visited.get(value);
|
||||
if (visitedIndex == null) {
|
||||
ctx.visited.put(value, ctx.callCount);
|
||||
if (ctx.maxDepth == 0 || ctx.callCount < ctx.maxDepth) {
|
||||
return dump(value, ctx);
|
||||
} else {
|
||||
return "<Reached max recursion depth>";
|
||||
}
|
||||
} else {
|
||||
return "<Previously visited - see hashCode " + value.hashCode() + ">";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static String getSimpleNameWithoutArrayQualifier(@SuppressWarnings("rawtypes") Class clazz) {
|
||||
String simpleName = clazz.getSimpleName();
|
||||
int indexOfBracket = simpleName.indexOf('[');
|
||||
if (indexOfBracket != -1)
|
||||
return simpleName.substring(0, indexOfBracket);
|
||||
return simpleName;
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.widget.ImageView;
|
||||
|
||||
public class GoNoGoLights {
|
||||
private Boolean state;
|
||||
private Boolean missing;
|
||||
private Boolean set;
|
||||
|
||||
private ImageView red;
|
||||
private ImageView green;
|
||||
|
||||
private Drawable dRed;
|
||||
private Drawable dGreen;
|
||||
private Drawable dGray;
|
||||
|
||||
public GoNoGoLights(ImageView in_red, ImageView in_green, Resources r) {
|
||||
red = in_red;
|
||||
green = in_green;
|
||||
state = false;
|
||||
missing = true;
|
||||
set = false;
|
||||
|
||||
dRed = in_red.getContext().getDrawable(R.drawable.redled);
|
||||
dGreen = in_red.getContext().getDrawable(R.drawable.greenled);
|
||||
dGray = in_red.getContext().getDrawable(R.drawable.grayled);
|
||||
}
|
||||
|
||||
public void set(Boolean s, Boolean m) {
|
||||
if (set && s == state && m == missing) return;
|
||||
state = s;
|
||||
missing = m;
|
||||
set = true;
|
||||
if (missing) {
|
||||
red.setImageDrawable(dGray);
|
||||
green.setImageDrawable(dGray);
|
||||
} else if (state) {
|
||||
red.setImageDrawable(dGray);
|
||||
green.setImageDrawable(dGreen);
|
||||
} else {
|
||||
red.setImageDrawable(dRed);
|
||||
green.setImageDrawable(dGray);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.*;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class IdleModeActivity extends Activity {
|
||||
private EditText callsignText;
|
||||
private TextView frequencyView;
|
||||
private Button connect;
|
||||
private Button disconnect;
|
||||
private Button reboot;
|
||||
private Button igniters;
|
||||
private double frequency;
|
||||
|
||||
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 callsignText.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) {
|
||||
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Setup the window
|
||||
setContentView(R.layout.idle_mode);
|
||||
|
||||
callsignText = (EditText) findViewById(R.id.set_callsign);
|
||||
callsignText.setText(new StringBuffer(AltosPreferences.callsign()));
|
||||
|
||||
frequency = getIntent().getDoubleExtra(AltosDroid.EXTRA_FREQUENCY, 0.0);
|
||||
frequencyView = (TextView) findViewById(R.id.frequency);
|
||||
frequencyView.setText(String.format("Frequency: %7.3f MHz", frequency));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 android.app.Activity;
|
||||
import android.content.*;
|
||||
import android.graphics.*;
|
||||
import android.os.*;
|
||||
import android.view.*;
|
||||
import android.view.View.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
class IgniterItem {
|
||||
public String name;
|
||||
public String pretty;
|
||||
public String status;
|
||||
public LinearLayout igniter_view = null;
|
||||
public TextView pretty_view = null;
|
||||
public TextView status_view = null;
|
||||
|
||||
private void update() {
|
||||
if (pretty_view != null)
|
||||
pretty_view.setText(pretty);
|
||||
if (status_view != null)
|
||||
status_view.setText(status);
|
||||
}
|
||||
|
||||
public void set(String name, String pretty, String status) {
|
||||
if (!name.equals(this.name) ||
|
||||
!pretty.equals(this.pretty) ||
|
||||
!status.equals(this.status))
|
||||
{
|
||||
this.name = name;
|
||||
this.pretty = pretty;
|
||||
this.status = status;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
public void realize(LinearLayout igniter_view,
|
||||
TextView pretty_view,
|
||||
TextView status_view) {
|
||||
if (igniter_view != this.igniter_view ||
|
||||
pretty_view != this.pretty_view ||
|
||||
status_view != this.status_view)
|
||||
{
|
||||
this.igniter_view = igniter_view;
|
||||
this.pretty_view = pretty_view;
|
||||
this.status_view = status_view;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
public IgniterItem() {
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
if (position == selected_item)
|
||||
item.igniter_view.setBackgroundColor(Color.RED);
|
||||
else
|
||||
item.igniter_view.setBackgroundColor(0);
|
||||
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 Messenger service = null;
|
||||
private final Messenger messenger = new Messenger(new IncomingHandler(this));
|
||||
|
||||
private Timer query_timer;
|
||||
private boolean query_timer_running;
|
||||
|
||||
private Timer arm_timer;
|
||||
private int arm_remaining;
|
||||
|
||||
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:
|
||||
@SuppressWarnings("unchecked") HashMap<String,Integer> map = (HashMap <String,Integer>) msg.obj;
|
||||
ia.igniter_status(map);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private ServiceConnection connection = new ServiceConnection() {
|
||||
public void onServiceConnected(ComponentName className, IBinder binder) {
|
||||
service = new Messenger(binder);
|
||||
query_timer_tick();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
class FireThread extends Thread {
|
||||
private final String igniter;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Message msg = Message.obtain(null, TelemetryService.MSG_IGNITER_FIRE, igniter);
|
||||
try {
|
||||
service.send(msg);
|
||||
} catch (RemoteException re) {
|
||||
}
|
||||
}
|
||||
|
||||
public FireThread(String igniter) {
|
||||
this.igniter = igniter;
|
||||
}
|
||||
}
|
||||
|
||||
private void fire_igniter() {
|
||||
if (igniters_adapter.selected_item >= 0) {
|
||||
IgniterItem item = igniters_adapter.getItem(igniters_adapter.selected_item);
|
||||
FireThread ft = new FireThread(item.name);
|
||||
ft.run();
|
||||
arm.setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void arm_igniter(boolean is_checked) {
|
||||
if (is_checked) {
|
||||
arm_timer_stop();
|
||||
arm_timer = new Timer();
|
||||
arm_remaining = 10;
|
||||
arm_set_text();
|
||||
fire.setEnabled(true);
|
||||
arm_timer.scheduleAtFixedRate(new TimerTask() {
|
||||
public void run() {
|
||||
arm_timer_tick();
|
||||
}},
|
||||
1000L, 1000L);
|
||||
} else {
|
||||
arm_timer_stop();
|
||||
fire.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void query_timer_tick() {
|
||||
if (query_timer_running)
|
||||
return;
|
||||
if (service == null)
|
||||
return;
|
||||
query_timer_running = true;
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
Message msg = Message.obtain(null, TelemetryService.MSG_IGNITER_QUERY);
|
||||
msg.replyTo = messenger;
|
||||
if (service == null) {
|
||||
synchronized(IgniterActivity.this) {
|
||||
query_timer_running = false;
|
||||
}
|
||||
} else
|
||||
service.send(msg);
|
||||
} catch (RemoteException re) {
|
||||
AltosDebug.debug("igniter query thread failed");
|
||||
synchronized(IgniterActivity.this) {
|
||||
query_timer_running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}
|
||||
|
||||
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(name, pretty, AltosIgnite.status_string(status.get(name)));
|
||||
return true;
|
||||
}
|
||||
|
||||
private synchronized void igniter_status(HashMap <String,Integer> status) {
|
||||
query_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;
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void arm_timer_stop() {
|
||||
if (arm_timer != null) {
|
||||
arm_timer.cancel();
|
||||
arm_timer = null;
|
||||
}
|
||||
arm_remaining = 0;
|
||||
}
|
||||
|
||||
private void arm_set_text() {
|
||||
String text = String.format("Armed %d", arm_remaining);
|
||||
|
||||
if (arm.isChecked())
|
||||
arm.setText(text);
|
||||
arm.setTextOn(text);
|
||||
}
|
||||
|
||||
private void arm_timer_tick() {
|
||||
--arm_remaining;
|
||||
if (arm_remaining <= 0) {
|
||||
arm_timer_stop();
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
arm.setChecked(false);
|
||||
fire.setEnabled(false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
arm_set_text();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void select_item(int position) {
|
||||
if (position != igniters_adapter.selected_item) {
|
||||
if (igniters_adapter.selected_item >= 0)
|
||||
igniters_view.setItemChecked(igniters_adapter.selected_item, false);
|
||||
if (position >= 0) {
|
||||
igniters_view.setItemChecked(position, true);
|
||||
arm.setEnabled(true);
|
||||
} else
|
||||
arm.setEnabled(false);
|
||||
igniters_adapter.selected_item = position;
|
||||
}
|
||||
}
|
||||
|
||||
private class IgniterItemClickListener implements ListView.OnItemClickListener {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> av, View v, int position, long id) {
|
||||
AltosDebug.debug("select %d\n", position);
|
||||
select_item(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Setup the window
|
||||
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.setEnabled(false);
|
||||
fire.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
fire_igniter();
|
||||
}
|
||||
});
|
||||
|
||||
arm = (ToggleButton) findViewById(R.id.igniter_arm);
|
||||
arm.setEnabled(false);
|
||||
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();
|
||||
query_timer = new Timer(true);
|
||||
query_timer.scheduleAtFixedRate(new TimerTask() {
|
||||
public void run() {
|
||||
query_timer_tick();
|
||||
}},
|
||||
0L, 5000L);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
if (query_timer != null) {
|
||||
query_timer.cancel();
|
||||
query_timer = null;
|
||||
}
|
||||
arm_timer_stop();
|
||||
arm.setChecked(false);
|
||||
fire.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
doUnbindService();
|
||||
}
|
||||
}
|
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 java.text.*;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.*;
|
||||
import android.graphics.*;
|
||||
import android.os.*;
|
||||
import android.view.*;
|
||||
import android.view.View.*;
|
||||
import android.view.inputmethod.*;
|
||||
import android.widget.*;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
class FrequencyItem {
|
||||
public AltosFrequency frequency;
|
||||
public LinearLayout frequency_view = null;
|
||||
public TextView pretty_view = null;
|
||||
|
||||
private void update() {
|
||||
if (pretty_view != null && frequency != null)
|
||||
pretty_view.setText(frequency.toString());
|
||||
}
|
||||
|
||||
public void realize(LinearLayout frequency_view,
|
||||
TextView pretty_view) {
|
||||
if (frequency_view != this.frequency_view ||
|
||||
pretty_view != this.pretty_view)
|
||||
{
|
||||
this.frequency_view = frequency_view;
|
||||
this.pretty_view = pretty_view;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
public void set_frequency(AltosFrequency frequency) {
|
||||
this.frequency = frequency;
|
||||
update();
|
||||
}
|
||||
|
||||
public FrequencyItem(AltosFrequency frequency) {
|
||||
this.frequency = frequency;
|
||||
}
|
||||
}
|
||||
|
||||
class FrequencyAdapter extends ArrayAdapter<FrequencyItem> {
|
||||
int resource;
|
||||
int selected_item = -1;
|
||||
|
||||
public FrequencyAdapter(Context context, int in_resource) {
|
||||
super(context, in_resource);
|
||||
resource = in_resource;
|
||||
}
|
||||
|
||||
public int count() {
|
||||
int count;
|
||||
|
||||
for (count = 0;; count++) {
|
||||
try {
|
||||
getItem(count);
|
||||
} catch (IndexOutOfBoundsException ie) {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
FrequencyItem item = getItem(position);
|
||||
if (item.frequency_view == null) {
|
||||
LinearLayout frequency_view = new LinearLayout(getContext());
|
||||
String inflater = Context.LAYOUT_INFLATER_SERVICE;
|
||||
LayoutInflater li = (LayoutInflater) getContext().getSystemService(inflater);
|
||||
li.inflate(resource, frequency_view, true);
|
||||
|
||||
item.realize(frequency_view,
|
||||
(TextView) frequency_view.findViewById(R.id.frequency));
|
||||
}
|
||||
if (position == selected_item)
|
||||
item.frequency_view.setBackgroundColor(Color.RED);
|
||||
else
|
||||
item.frequency_view.setBackgroundColor(0);
|
||||
return item.frequency_view;
|
||||
}
|
||||
}
|
||||
|
||||
public class ManageFrequenciesActivity extends Activity {
|
||||
private ListView frequencies_view;
|
||||
|
||||
private Button set;
|
||||
private Button remove;
|
||||
private Button done;
|
||||
|
||||
private EditText set_frequency;
|
||||
private EditText set_description;
|
||||
|
||||
private HashMap<String,FrequencyItem> frequencies = new HashMap<String,FrequencyItem>();;
|
||||
|
||||
private FrequencyAdapter frequencies_adapter;
|
||||
|
||||
private boolean is_bound;
|
||||
private boolean changed = false;
|
||||
|
||||
private void done() {
|
||||
|
||||
set();
|
||||
|
||||
if (changed) {
|
||||
AltosFrequency[] frequencies = new AltosFrequency[frequencies_adapter.count()];
|
||||
for (int i = 0; i < frequencies.length; i++)
|
||||
frequencies[i] = frequencies_adapter.getItem(i).frequency;
|
||||
AltosPreferences.set_common_frequencies(frequencies);
|
||||
}
|
||||
|
||||
Intent intent = new Intent();
|
||||
setResult(Activity.RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void load_item() {
|
||||
if (frequencies_adapter.selected_item >= 0) {
|
||||
FrequencyItem item = frequencies_adapter.getItem(frequencies_adapter.selected_item);
|
||||
|
||||
set_frequency.setText(item.frequency.frequency_string());
|
||||
set_description.setText(item.frequency.description);
|
||||
} else {
|
||||
set_frequency.setText("");
|
||||
set_description.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
private void select_item(int position) {
|
||||
if (position != frequencies_adapter.selected_item) {
|
||||
if (frequencies_adapter.selected_item >= 0)
|
||||
frequencies_view.setItemChecked(frequencies_adapter.selected_item, false);
|
||||
if (position >= 0)
|
||||
frequencies_view.setItemChecked(position, true);
|
||||
frequencies_adapter.selected_item = position;
|
||||
} else {
|
||||
if (frequencies_adapter.selected_item >= 0)
|
||||
frequencies_view.setItemChecked(frequencies_adapter.selected_item, false);
|
||||
frequencies_adapter.selected_item = -1;
|
||||
}
|
||||
load_item();
|
||||
}
|
||||
|
||||
private int find(AltosFrequency frequency) {
|
||||
for (int pos = 0; pos < frequencies_adapter.getCount(); pos++) {
|
||||
FrequencyItem item = frequencies_adapter.getItem(pos);
|
||||
if (item.frequency.frequency == frequency.frequency &&
|
||||
item.frequency.description.equals(frequency.description))
|
||||
return pos;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int insert_item(AltosFrequency frequency) {
|
||||
FrequencyItem new_item = new FrequencyItem(frequency);
|
||||
int pos;
|
||||
for (pos = 0; pos < frequencies_adapter.getCount(); pos++) {
|
||||
FrequencyItem item = frequencies_adapter.getItem(pos);
|
||||
if (item.frequency.frequency == new_item.frequency.frequency) {
|
||||
item.set_frequency(frequency);
|
||||
return pos;
|
||||
}
|
||||
if (item.frequency.frequency > new_item.frequency.frequency)
|
||||
break;
|
||||
}
|
||||
frequencies_adapter.insert(new_item, pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
private class FrequencyItemClickListener implements ListView.OnItemClickListener {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> av, View v, int position, long id) {
|
||||
select_item(position);
|
||||
}
|
||||
}
|
||||
|
||||
private void hide_keyboard() {
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||
View view = getCurrentFocus();
|
||||
if (view != null)
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
private void set() {
|
||||
String frequency_text = set_frequency.getEditableText().toString();
|
||||
String description_text = set_description.getEditableText().toString();
|
||||
|
||||
try {
|
||||
double f = AltosParse.parse_double_locale(frequency_text);
|
||||
AltosFrequency frequency = new AltosFrequency(f, description_text);
|
||||
int pos;
|
||||
|
||||
pos = find(frequency);
|
||||
if (pos < 0) {
|
||||
pos = insert_item(frequency);
|
||||
changed = true;
|
||||
}
|
||||
frequencies_adapter.selected_item = -1;
|
||||
select_item(pos);
|
||||
} catch (ParseException pe) {
|
||||
}
|
||||
hide_keyboard();
|
||||
}
|
||||
|
||||
private void remove() {
|
||||
if (frequencies_adapter.selected_item >= 0) {
|
||||
frequencies_adapter.remove(frequencies_adapter.getItem(frequencies_adapter.selected_item));
|
||||
select_item(-1);
|
||||
frequencies_view.setAdapter(frequencies_adapter);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Setup the window
|
||||
setContentView(R.layout.manage_frequencies);
|
||||
|
||||
frequencies_view = (ListView) findViewById(R.id.frequencies);
|
||||
frequencies_view.setClickable(true);
|
||||
|
||||
frequencies_adapter = new FrequencyAdapter(this, R.layout.frequency);
|
||||
|
||||
frequencies_view.setAdapter(frequencies_adapter);
|
||||
frequencies_view.setOnItemClickListener(new FrequencyItemClickListener());
|
||||
|
||||
AltosFrequency[] frequencies = AltosPreferences.common_frequencies();
|
||||
for (AltosFrequency frequency : frequencies)
|
||||
insert_item(frequency);
|
||||
|
||||
set_frequency = (EditText) findViewById(R.id.set_frequency);
|
||||
set_description = (EditText) findViewById(R.id.set_description);
|
||||
|
||||
set = (Button) findViewById(R.id.set);
|
||||
set.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
set();
|
||||
}
|
||||
});
|
||||
|
||||
remove = (Button) findViewById(R.id.remove);
|
||||
remove.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
remove();
|
||||
}
|
||||
});
|
||||
|
||||
done = (Button) findViewById(R.id.done);
|
||||
done.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
// Set result CANCELED incase the user backs out
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright © 2015 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.*;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class MapTypeActivity extends Activity {
|
||||
private Button hybrid;
|
||||
private Button satellite;
|
||||
private Button roadmap;
|
||||
private Button terrain;
|
||||
private int selected_type;
|
||||
|
||||
public static final String EXTRA_MAP_TYPE = "map_type";
|
||||
|
||||
private void done(int type) {
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(EXTRA_MAP_TYPE, type);
|
||||
setResult(Activity.RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
public void selectType(View view) {
|
||||
AltosDebug.debug("selectType %s", view.toString());
|
||||
if (view == hybrid)
|
||||
done(AltosMap.maptype_hybrid);
|
||||
if (view == satellite)
|
||||
done(AltosMap.maptype_satellite);
|
||||
if (view == roadmap)
|
||||
done(AltosMap.maptype_roadmap);
|
||||
if (view == terrain)
|
||||
done(AltosMap.maptype_terrain);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Setup the window
|
||||
setContentView(R.layout.map_type);
|
||||
|
||||
hybrid = (Button) findViewById(R.id.map_type_hybrid);
|
||||
satellite = (Button) findViewById(R.id.map_type_satellite);
|
||||
roadmap = (Button) findViewById(R.id.map_type_roadmap);
|
||||
terrain = (Button) findViewById(R.id.map_type_terrain);
|
||||
|
||||
// Set result CANCELED incase the user backs out
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
}
|
||||
}
|
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright © 2015 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 java.text.*;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
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 android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.location.LocationListener;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
/**
|
||||
* This Activity appears as a dialog. It lists any paired devices and
|
||||
* devices detected in the area after discovery. When a device is chosen
|
||||
* by the user, the MAC address of the device is sent back to the parent
|
||||
* Activity in the result Intent.
|
||||
*/
|
||||
public class PreloadMapActivity extends Activity implements AltosLaunchSiteListener, AltosMapLoaderListener, LocationListener {
|
||||
|
||||
private ArrayAdapter<AltosLaunchSite> known_sites_adapter;
|
||||
|
||||
/*
|
||||
private CheckBox hybrid;
|
||||
private CheckBox satellite;
|
||||
private CheckBox roadmap;
|
||||
private CheckBox terrain;
|
||||
*/
|
||||
|
||||
private Spinner known_sites_spinner;
|
||||
private Spinner min_zoom;
|
||||
private Spinner max_zoom;
|
||||
private TextView radius_label;
|
||||
private Spinner radius;
|
||||
|
||||
private EditText latitude;
|
||||
private EditText longitude;
|
||||
|
||||
private ProgressBar progress;
|
||||
|
||||
private AltosMapLoader loader;
|
||||
|
||||
long loader_notify_time;
|
||||
|
||||
/* AltosMapLoaderListener interfaces */
|
||||
public void loader_start(final int max) {
|
||||
loader_notify_time = System.currentTimeMillis();
|
||||
|
||||
this.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
progress.setMax(max);
|
||||
progress.setProgress(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void loader_notify(final int cur, final int max, final String name) {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (now - loader_notify_time < 100)
|
||||
return;
|
||||
|
||||
loader_notify_time = now;
|
||||
|
||||
this.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
progress.setProgress(cur);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void loader_done(int max) {
|
||||
loader = null;
|
||||
this.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
progress.setProgress(0);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void debug(String format, Object ... arguments) {
|
||||
AltosDebug.debug(format, arguments);
|
||||
}
|
||||
|
||||
/* AltosLaunchSiteListener interface */
|
||||
|
||||
public void notify_launch_sites(final List<AltosLaunchSite> sites) {
|
||||
this.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
for (AltosLaunchSite site : sites)
|
||||
known_sites_adapter.add(site);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* LocationProvider interface */
|
||||
|
||||
AltosLaunchSite current_location_site;
|
||||
|
||||
public void onLocationChanged(Location location) {
|
||||
AltosDebug.debug("location changed");
|
||||
if (current_location_site == null) {
|
||||
AltosLaunchSite selected_item = (AltosLaunchSite) known_sites_spinner.getSelectedItem();
|
||||
|
||||
current_location_site = new AltosLaunchSite("Current Location", location.getLatitude(), location.getLongitude());
|
||||
known_sites_adapter.insert(current_location_site, 0);
|
||||
|
||||
if (selected_item != null)
|
||||
known_sites_spinner.setSelection(known_sites_adapter.getPosition(selected_item));
|
||||
else {
|
||||
latitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.latitude)));
|
||||
longitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.longitude)));
|
||||
}
|
||||
} else {
|
||||
current_location_site.latitude = location.getLatitude();
|
||||
current_location_site.longitude = location.getLongitude();
|
||||
}
|
||||
}
|
||||
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
}
|
||||
|
||||
public void onProviderEnabled(String provider) {
|
||||
}
|
||||
|
||||
public void onProviderDisabled(String provider) {
|
||||
}
|
||||
|
||||
private double text(EditText view) throws ParseException {
|
||||
return AltosParse.parse_double_locale(view.getEditableText().toString());
|
||||
}
|
||||
|
||||
private double latitude() throws ParseException {
|
||||
return text(latitude);
|
||||
}
|
||||
|
||||
private double longitude() throws ParseException {
|
||||
return text(longitude);
|
||||
}
|
||||
|
||||
private int value(Spinner spinner) {
|
||||
return (Integer) spinner.getSelectedItem();
|
||||
}
|
||||
|
||||
private int min_z() {
|
||||
return value(min_zoom);
|
||||
}
|
||||
|
||||
private int max_z() {
|
||||
return value(max_zoom);
|
||||
}
|
||||
|
||||
private double value_distance(Spinner spinner) {
|
||||
return (Double) spinner.getSelectedItem();
|
||||
}
|
||||
|
||||
private double radius() {
|
||||
double r = value_distance(radius);
|
||||
if (AltosPreferences.imperial_units())
|
||||
r = AltosConvert.miles_to_meters(r);
|
||||
else
|
||||
r = r * 1000;
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
private int bit(CheckBox box, int value) {
|
||||
if (box.isChecked())
|
||||
return 1 << value;
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
private int types() {
|
||||
/*
|
||||
return (bit(hybrid, AltosMap.maptype_hybrid) |
|
||||
bit(satellite, AltosMap.maptype_satellite) |
|
||||
bit(roadmap, AltosMap.maptype_roadmap) |
|
||||
bit(terrain, AltosMap.maptype_terrain));
|
||||
*/
|
||||
return 1 << AltosMap.maptype_hybrid;
|
||||
}
|
||||
|
||||
private void load() {
|
||||
if (loader != null)
|
||||
return;
|
||||
|
||||
try {
|
||||
double lat = latitude();
|
||||
double lon = longitude();
|
||||
int min = min_z();
|
||||
int max = max_z();
|
||||
double r = radius();
|
||||
int t = types();
|
||||
|
||||
AltosDebug.debug("PreloadMap load %f %f %d %d %f %d\n",
|
||||
lat, lon, min, max, r, t);
|
||||
loader = new AltosMapLoader(this, lat, lon, min, max, r, t, AltosMapOffline.scale);
|
||||
} catch (ParseException e) {
|
||||
AltosDebug.debug("PreloadMap load raised exception %s", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void add_numbers(Spinner spinner, int min, int max, int def) {
|
||||
|
||||
ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this, android.R.layout.simple_spinner_item);
|
||||
|
||||
int spinner_def = 0;
|
||||
int pos = 0;
|
||||
|
||||
for (int i = min; i <= max; i++) {
|
||||
adapter.add(new Integer(i));
|
||||
if (i == def)
|
||||
spinner_def = pos;
|
||||
pos++;
|
||||
}
|
||||
|
||||
spinner.setAdapter(adapter);
|
||||
spinner.setSelection(spinner_def);
|
||||
}
|
||||
|
||||
|
||||
private void add_distance(Spinner spinner, double[] distances_km, double def_km, double[] distances_mi, double def_mi) {
|
||||
|
||||
ArrayAdapter<Double> adapter = new ArrayAdapter<Double>(this, android.R.layout.simple_spinner_item);
|
||||
|
||||
int spinner_def = 0;
|
||||
int pos = 0;
|
||||
|
||||
double[] distances;
|
||||
double def;
|
||||
if (AltosPreferences.imperial_units()) {
|
||||
distances = distances_mi;
|
||||
def = def_mi;
|
||||
} else {
|
||||
distances = distances_km;
|
||||
def = def_km;
|
||||
}
|
||||
|
||||
for (int i = 0; i < distances.length; i++) {
|
||||
adapter.add(distances[i]);
|
||||
if (distances[i] == def)
|
||||
spinner_def = pos;
|
||||
pos++;
|
||||
}
|
||||
|
||||
spinner.setAdapter(adapter);
|
||||
spinner.setSelection(spinner_def);
|
||||
}
|
||||
|
||||
|
||||
|
||||
class SiteListListener implements OnItemSelectedListener {
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||
AltosLaunchSite site = (AltosLaunchSite) parent.getItemAtPosition(pos);
|
||||
latitude.setText(new StringBuffer(String.format("%12.6f", site.latitude)));
|
||||
longitude.setText(new StringBuffer(String.format("%12.6f", site.longitude)));
|
||||
}
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
|
||||
public SiteListListener() {
|
||||
}
|
||||
}
|
||||
|
||||
double[] radius_mi = { 1, 2, 5, 10, 20 };
|
||||
double radius_def_mi = 2;
|
||||
double[] radius_km = { 1, 2, 5, 10, 20, 30 };
|
||||
double radius_def_km = 2;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Setup the window
|
||||
setContentView(R.layout.map_preload);
|
||||
|
||||
// Set result CANCELED incase the user backs out
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
|
||||
// Initialize the button to perform device discovery
|
||||
Button loadButton = (Button) findViewById(R.id.preload_load);
|
||||
loadButton.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
load();
|
||||
}
|
||||
});
|
||||
|
||||
latitude = (EditText) findViewById(R.id.preload_latitude);
|
||||
longitude = (EditText) findViewById(R.id.preload_longitude);
|
||||
|
||||
/*
|
||||
hybrid = (CheckBox) findViewById(R.id.preload_hybrid);
|
||||
satellite = (CheckBox) findViewById(R.id.preload_satellite);
|
||||
roadmap = (CheckBox) findViewById(R.id.preload_roadmap);
|
||||
terrain = (CheckBox) findViewById(R.id.preload_terrain);
|
||||
|
||||
hybrid.setChecked(true);
|
||||
*/
|
||||
|
||||
min_zoom = (Spinner) findViewById(R.id.preload_min_zoom);
|
||||
add_numbers(min_zoom,
|
||||
AltosMap.min_zoom - AltosMap.default_zoom,
|
||||
AltosMap.max_zoom - AltosMap.default_zoom, -2);
|
||||
max_zoom = (Spinner) findViewById(R.id.preload_max_zoom);
|
||||
add_numbers(max_zoom,
|
||||
AltosMap.min_zoom - AltosMap.default_zoom,
|
||||
AltosMap.max_zoom - AltosMap.default_zoom, 2);
|
||||
radius_label = (TextView) findViewById(R.id.preload_radius_label);
|
||||
radius = (Spinner) findViewById(R.id.preload_radius);
|
||||
if (AltosPreferences.imperial_units())
|
||||
radius_label.setText("Radius (miles)");
|
||||
else
|
||||
radius_label.setText("Radius (km)");
|
||||
add_distance(radius, radius_km, radius_def_km, radius_mi, radius_def_mi);
|
||||
|
||||
progress = (ProgressBar) findViewById(R.id.preload_progress);
|
||||
|
||||
// Initialize array adapters. One for already paired devices and
|
||||
// one for newly discovered devices
|
||||
known_sites_spinner = (Spinner) findViewById(R.id.preload_site_list);
|
||||
|
||||
known_sites_adapter = new ArrayAdapter<AltosLaunchSite>(this, android.R.layout.simple_spinner_item);
|
||||
|
||||
known_sites_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
|
||||
known_sites_spinner.setAdapter(known_sites_adapter);
|
||||
known_sites_spinner.setOnItemSelectedListener(new SiteListListener());
|
||||
|
||||
// Listen for GPS and Network position updates
|
||||
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
|
||||
try {
|
||||
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
|
||||
} catch (Exception e) {
|
||||
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 1, this);
|
||||
}
|
||||
|
||||
new AltosLaunchSites(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (loader != null)
|
||||
loader.abort();
|
||||
|
||||
// Stop listening for location updates
|
||||
((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
|
||||
}
|
||||
}
|
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright © 2020 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, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 android.app.Activity;
|
||||
import android.content.*;
|
||||
import android.os.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
import android.view.View.*;
|
||||
import android.widget.*;
|
||||
import android.graphics.*;
|
||||
import android.graphics.drawable.*;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
class TrackerComparatorCall implements Comparator<Tracker> {
|
||||
public int compare(Tracker a, Tracker b) {
|
||||
int v;
|
||||
|
||||
v = a.compareCall(b);
|
||||
if (v != 0)
|
||||
return v;
|
||||
v = a.compareAge(b);
|
||||
if (v != 0)
|
||||
return v;
|
||||
v = a.compareSerial(b);
|
||||
if (v != 0)
|
||||
return v;
|
||||
return a.compareFrequency(b);
|
||||
}
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof TrackerComparatorCall;
|
||||
}
|
||||
}
|
||||
|
||||
class TrackerComparatorSerial implements Comparator<Tracker> {
|
||||
public int compare(Tracker a, Tracker b) {
|
||||
int v;
|
||||
|
||||
v = a.compareSerial(b);
|
||||
if (v != 0)
|
||||
return v;
|
||||
v = a.compareAge(b);
|
||||
if (v != 0)
|
||||
return v;
|
||||
v = a.compareCall(b);
|
||||
if (v != 0)
|
||||
return v;
|
||||
return a.compareFrequency(b);
|
||||
}
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof TrackerComparatorSerial;
|
||||
}
|
||||
}
|
||||
|
||||
class TrackerComparatorAge implements Comparator<Tracker> {
|
||||
public int compare(Tracker a, Tracker b) {
|
||||
int v;
|
||||
|
||||
v = a.compareAge(b);
|
||||
if (v != 0)
|
||||
return v;
|
||||
v = a.compareCall(b);
|
||||
if (v != 0)
|
||||
return v;
|
||||
v = a.compareSerial(b);
|
||||
if (v != 0)
|
||||
return v;
|
||||
return a.compareFrequency(b);
|
||||
}
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof TrackerComparatorAge;
|
||||
}
|
||||
}
|
||||
|
||||
class TrackerComparatorFrequency implements Comparator<Tracker> {
|
||||
public int compare(Tracker a, Tracker b) {
|
||||
int v;
|
||||
|
||||
v = a.compareFrequency(b);
|
||||
if (v != 0)
|
||||
return v;
|
||||
v = a.compareAge(b);
|
||||
if (v != 0)
|
||||
return v;
|
||||
v = a.compareCall(b);
|
||||
if (v != 0)
|
||||
return v;
|
||||
return a.compareSerial(b);
|
||||
}
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof TrackerComparatorFrequency;
|
||||
}
|
||||
}
|
||||
|
||||
public class SelectTrackerActivity extends Activity implements OnTouchListener {
|
||||
// Return Intent extra
|
||||
public static final String EXTRA_SERIAL_NUMBER = "serial_number";
|
||||
public static final String EXTRA_FREQUENCY = "frequency";
|
||||
|
||||
private int button_ids[] = {
|
||||
R.id.call_button,
|
||||
R.id.serial_button,
|
||||
R.id.frequency_button,
|
||||
R.id.age_button
|
||||
};
|
||||
|
||||
private static final int call_button = 0;
|
||||
private static final int serial_button = 1;
|
||||
private static final int freq_button = 2;
|
||||
private static final int age_button = 3;
|
||||
private RadioButton radio_buttons[] = new RadioButton[4];
|
||||
private TableLayout table;
|
||||
|
||||
private Tracker[] trackers;
|
||||
|
||||
private void set_sort(int id) {
|
||||
AltosDroidPreferences.set_tracker_sort(id);
|
||||
resort();
|
||||
}
|
||||
|
||||
private void resort() {
|
||||
Comparator<Tracker> compare;
|
||||
int tracker_sort = AltosDroidPreferences.tracker_sort();
|
||||
AltosDebug.debug("sort %d", tracker_sort);
|
||||
switch (tracker_sort) {
|
||||
case call_button:
|
||||
default:
|
||||
compare = new TrackerComparatorCall();
|
||||
break;
|
||||
case serial_button:
|
||||
compare = new TrackerComparatorSerial();
|
||||
break;
|
||||
case freq_button:
|
||||
compare = new TrackerComparatorFrequency();
|
||||
break;
|
||||
case age_button:
|
||||
compare = new TrackerComparatorAge();
|
||||
break;
|
||||
}
|
||||
Arrays.sort(trackers, compare);
|
||||
set_trackers();
|
||||
}
|
||||
|
||||
void init_button_state() {
|
||||
int tracker_sort = AltosDroidPreferences.tracker_sort();
|
||||
for (int i = 0; i < 4; i++)
|
||||
radio_buttons[i].setChecked(i == tracker_sort);
|
||||
}
|
||||
|
||||
OnCheckedChangeListener button_listener = new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
int id = buttonView.getId();
|
||||
if (isChecked) {
|
||||
int sort_id = -1;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (id == button_ids[i])
|
||||
sort_id = i;
|
||||
else
|
||||
radio_buttons[i].setChecked(false);
|
||||
}
|
||||
if (sort_id != -1)
|
||||
set_sort(sort_id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
long start_time;
|
||||
|
||||
private void
|
||||
insert_tracker(Tracker tracker) {
|
||||
TableRow row = (TableRow) getLayoutInflater().inflate(R.layout.tracker_ent, null);
|
||||
|
||||
((TextView) row.findViewById(R.id.call_view)).setText(tracker.call);
|
||||
if (tracker.serial == 0)
|
||||
((TextView) row.findViewById(R.id.serial_view)).setText("");
|
||||
else
|
||||
((TextView) row.findViewById(R.id.serial_view)).setText(String.format("%d", tracker.serial));
|
||||
if (tracker.frequency == 0.0)
|
||||
((TextView) row.findViewById(R.id.frequency_view)).setText("");
|
||||
else if (tracker.frequency == AltosLib.MISSING)
|
||||
((TextView) row.findViewById(R.id.frequency_view)).setText("");
|
||||
else
|
||||
((TextView) row.findViewById(R.id.frequency_view)).setText(String.format("%7.3f", tracker.frequency));
|
||||
if (tracker.received_time != 0) {
|
||||
int age = (int) ((start_time - tracker.received_time + 500) / 1000);
|
||||
((TextView) row.findViewById(R.id.age_view)).setText(AltosDroid.age_string(age));
|
||||
} else {
|
||||
((TextView) row.findViewById(R.id.age_view)).setText("");
|
||||
}
|
||||
row.setClickable(true);
|
||||
row.setOnTouchListener(this);
|
||||
table.addView(row);
|
||||
}
|
||||
|
||||
private void set_trackers() {
|
||||
for (int i = table.getChildCount() - 1; i >= 1; i--)
|
||||
table.removeViewAt(i);
|
||||
for (Tracker tracker : trackers)
|
||||
insert_tracker(tracker);
|
||||
}
|
||||
|
||||
private void done(View v) {
|
||||
int result = Activity.RESULT_CANCELED;
|
||||
Intent intent = new Intent();
|
||||
for (int i = 1; i < table.getChildCount(); i++) {
|
||||
View child = table.getChildAt(i);
|
||||
if (child == v) {
|
||||
Tracker tracker = trackers[i - 1];
|
||||
intent.putExtra(EXTRA_SERIAL_NUMBER, tracker.serial);
|
||||
intent.putExtra(EXTRA_FREQUENCY, tracker.frequency);
|
||||
result = Activity.RESULT_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
setResult(Activity.RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
int title_id = getIntent().getIntExtra(AltosDroid.EXTRA_TRACKERS_TITLE, R.id.select_tracker);
|
||||
AltosDebug.debug("get title id 0x%x %s", title_id, getResources().getText(title_id));
|
||||
setTitle(getResources().getText(title_id));
|
||||
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.tracker_list);
|
||||
// Set result CANCELED incase the user backs out
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
radio_buttons[i] = (RadioButton) findViewById(button_ids[i]);
|
||||
radio_buttons[i].setOnCheckedChangeListener(button_listener);
|
||||
}
|
||||
|
||||
ArrayList<Parcelable> tracker_array = (ArrayList<Parcelable>) getIntent().getParcelableArrayListExtra(AltosDroid.EXTRA_TRACKERS);
|
||||
if (tracker_array != null) {
|
||||
Object[] array = tracker_array.toArray();
|
||||
trackers = new Tracker[array.length];
|
||||
for (int i = 0; i < array.length; i++)
|
||||
trackers[i] = (Tracker) array[i];
|
||||
}
|
||||
|
||||
start_time = System.currentTimeMillis();
|
||||
|
||||
table = (TableLayout) findViewById(R.id.tracker_list);
|
||||
|
||||
init_button_state();
|
||||
|
||||
resort();
|
||||
|
||||
set_trackers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
int action = event.getAction() & MotionEvent.ACTION_MASK;
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
case MotionEvent.ACTION_OUTSIDE:
|
||||
v.setBackgroundColor(0);
|
||||
v.invalidate();
|
||||
break;
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
v.setBackgroundColor(Color.RED);
|
||||
v.invalidate();
|
||||
break;
|
||||
}
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
done(v);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 android.app.Activity;
|
||||
import android.content.*;
|
||||
import android.os.*;
|
||||
import android.view.*;
|
||||
import android.view.View.*;
|
||||
import android.widget.*;
|
||||
import android.widget.AdapterView.*;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class SetupActivity extends Activity {
|
||||
private Spinner select_rate;
|
||||
private Spinner set_units;
|
||||
private Spinner font_size;
|
||||
private Spinner map_type;
|
||||
private Spinner map_source;
|
||||
private Button manage_frequencies;
|
||||
private Button preload_maps;
|
||||
private Button done;
|
||||
|
||||
private boolean is_bound;
|
||||
private Messenger service = null;
|
||||
|
||||
public final static String EXTRA_SETUP_CHANGES = "setup_changes";
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static final String[] rates = {
|
||||
"38400",
|
||||
"9600",
|
||||
"2400",
|
||||
};
|
||||
|
||||
static final String[] sizes = {
|
||||
"Small",
|
||||
"Medium",
|
||||
"Large",
|
||||
"Extra"
|
||||
};
|
||||
|
||||
static final String[] map_types = {
|
||||
"Hybrid",
|
||||
"Satellite",
|
||||
"Roadmap",
|
||||
"Terrain"
|
||||
};
|
||||
|
||||
static final int[] map_type_values = {
|
||||
AltosMap.maptype_hybrid,
|
||||
AltosMap.maptype_satellite,
|
||||
AltosMap.maptype_roadmap,
|
||||
AltosMap.maptype_terrain,
|
||||
};
|
||||
|
||||
static final String[] map_sources = {
|
||||
"Online",
|
||||
"Offline"
|
||||
};
|
||||
|
||||
private int set_telemetry_rate;
|
||||
private int set_map_source;
|
||||
private int set_map_type;
|
||||
private boolean set_imperial_units;
|
||||
private int set_font_size;
|
||||
|
||||
private void done() {
|
||||
int changes = 0;
|
||||
Intent intent = new Intent();
|
||||
|
||||
if (set_telemetry_rate != AltosPreferences.telemetry_rate(1)) {
|
||||
changes |= AltosDroid.SETUP_BAUD;
|
||||
AltosPreferences.set_telemetry_rate(1, set_telemetry_rate);
|
||||
}
|
||||
if (set_imperial_units != AltosPreferences.imperial_units()) {
|
||||
changes |= AltosDroid.SETUP_UNITS;
|
||||
AltosPreferences.set_imperial_units(set_imperial_units);
|
||||
}
|
||||
if (set_map_source != AltosDroidPreferences.map_source()) {
|
||||
changes |= AltosDroid.SETUP_MAP_SOURCE;
|
||||
AltosDroidPreferences.set_map_source(set_map_source);
|
||||
}
|
||||
if (set_map_type != AltosPreferences.map_type()) {
|
||||
changes |= AltosDroid.SETUP_MAP_TYPE;
|
||||
AltosPreferences.set_map_type(set_map_type);
|
||||
}
|
||||
if (set_font_size != AltosDroidPreferences.font_size()) {
|
||||
changes |= AltosDroid.SETUP_FONT_SIZE;
|
||||
AltosDroidPreferences.set_font_size(set_font_size);
|
||||
}
|
||||
intent.putExtra(EXTRA_SETUP_CHANGES, changes);
|
||||
setResult(Activity.RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void add_strings(Spinner spinner, String[] strings, int def) {
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item);
|
||||
|
||||
for (int i = 0; i < strings.length; i++)
|
||||
adapter.add(strings[i]);
|
||||
|
||||
spinner.setAdapter(adapter);
|
||||
if (def >= 0)
|
||||
spinner.setSelection(def);
|
||||
}
|
||||
|
||||
private int default_rate_pos() {
|
||||
int default_rate = AltosPreferences.telemetry_rate(1);
|
||||
|
||||
for (int pos = 0; pos < rates.length; pos++) {
|
||||
if (string_to_rate(rates[pos]) == default_rate)
|
||||
return pos;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void setBaud(int baud) {
|
||||
set_telemetry_rate = baud;
|
||||
}
|
||||
|
||||
private int string_to_rate(String baud) {
|
||||
int rate = AltosLib.ao_telemetry_rate_38400;
|
||||
try {
|
||||
int value = Integer.parseInt(baud);
|
||||
switch (value) {
|
||||
case 2400:
|
||||
rate = AltosLib.ao_telemetry_rate_2400;
|
||||
break;
|
||||
case 9600:
|
||||
rate = AltosLib.ao_telemetry_rate_9600;
|
||||
break;
|
||||
case 38400:
|
||||
rate = AltosLib.ao_telemetry_rate_38400;
|
||||
break;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
private void setBaud(String baud) {
|
||||
setBaud(string_to_rate(baud));
|
||||
}
|
||||
|
||||
private void select_rate(int pos) {
|
||||
setBaud(rates[pos]);
|
||||
}
|
||||
|
||||
static final String[] units = {
|
||||
"Metric",
|
||||
"Imperial"
|
||||
};
|
||||
|
||||
private int default_units_pos() {
|
||||
boolean imperial = AltosPreferences.imperial_units();
|
||||
|
||||
if (imperial)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void set_units(int pos) {
|
||||
switch (pos) {
|
||||
default:
|
||||
set_imperial_units = false;
|
||||
break;
|
||||
case 1:
|
||||
set_imperial_units = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void set_font_size(int pos) {
|
||||
set_font_size = pos;
|
||||
}
|
||||
|
||||
private int default_map_type_pos() {
|
||||
int default_map_type = AltosPreferences.map_type();
|
||||
|
||||
for (int pos = 0; pos < map_types.length; pos++)
|
||||
if (map_type_values[pos] == default_map_type)
|
||||
return pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void select_map_type(int pos) {
|
||||
set_map_type = map_type_values[pos];
|
||||
}
|
||||
|
||||
private int default_map_source_pos() {
|
||||
int default_source = AltosDroidPreferences.map_source();
|
||||
|
||||
switch (default_source) {
|
||||
case AltosDroidPreferences.MAP_SOURCE_OFFLINE:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void select_map_source(int pos) {
|
||||
switch (pos) {
|
||||
default:
|
||||
set_map_source = AltosDroidPreferences.MAP_SOURCE_ONLINE;
|
||||
break;
|
||||
case 1:
|
||||
set_map_source = AltosDroidPreferences.MAP_SOURCE_OFFLINE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void manage_frequencies(){
|
||||
Intent intent = new Intent(this, ManageFrequenciesActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void preload_maps(){
|
||||
Intent intent = new Intent(this, PreloadMapActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
AltosDebug.init(this);
|
||||
AltosDebug.debug("+++ ON CREATE +++");
|
||||
|
||||
// Initialise preferences
|
||||
AltosDroidPreferences.init(this);
|
||||
|
||||
// Setup the window
|
||||
setContentView(R.layout.setup);
|
||||
|
||||
select_rate = (Spinner) findViewById(R.id.select_rate);
|
||||
add_strings(select_rate, rates, default_rate_pos());
|
||||
select_rate.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||
AltosDebug.debug("rate selected pos %d id %d", pos, id);
|
||||
select_rate(pos);
|
||||
}
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
});
|
||||
|
||||
set_units = (Spinner) findViewById(R.id.set_units);
|
||||
add_strings(set_units, units, default_units_pos());
|
||||
set_units.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||
set_units(pos);
|
||||
}
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
});
|
||||
|
||||
font_size = (Spinner) findViewById(R.id.font_size);
|
||||
add_strings(font_size, sizes, AltosDroidPreferences.font_size());
|
||||
font_size.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||
set_font_size(pos);
|
||||
}
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
});
|
||||
|
||||
map_type = (Spinner) findViewById(R.id.map_type);
|
||||
add_strings(map_type, map_types, default_map_type_pos());
|
||||
map_type.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||
select_map_type(pos);
|
||||
}
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
});
|
||||
|
||||
map_source = (Spinner) findViewById(R.id.map_source);
|
||||
add_strings(map_source, map_sources, default_map_source_pos());
|
||||
map_source.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||
select_map_source(pos);
|
||||
}
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
manage_frequencies = (Button) findViewById(R.id.manage_frequencies);
|
||||
manage_frequencies.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
manage_frequencies();
|
||||
}
|
||||
});
|
||||
|
||||
preload_maps = (Button) findViewById(R.id.preload_maps);
|
||||
preload_maps.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
preload_maps();
|
||||
}
|
||||
});
|
||||
|
||||
done = (Button) findViewById(R.id.done);
|
||||
done.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
// Set result for when the user backs out
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
doBindService();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
doUnbindService();
|
||||
}
|
||||
}
|
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 org.altusmetrum.altoslib_14.*;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import android.location.Location;
|
||||
|
||||
public class TabFlight extends AltosDroidTab {
|
||||
private TextView speed_view;
|
||||
private TextView height_view;
|
||||
private TextView altitude_view;
|
||||
private View tilt_view;
|
||||
private TextView tilt_value;
|
||||
private TextView max_speed_view;
|
||||
private TextView max_height_view;
|
||||
private TextView max_altitude_view;
|
||||
private TextView elevation_view;
|
||||
private TextView range_view;
|
||||
private TextView bearing_view;
|
||||
private TextView compass_view;
|
||||
private TextView distance_view;
|
||||
private TextView latitude_view;
|
||||
private TextView longitude_view;
|
||||
private View apogee_view;
|
||||
private TextView apogee_voltage_view;
|
||||
private TextView apogee_voltage_label;
|
||||
private GoNoGoLights apogee_lights;
|
||||
private View main_view;
|
||||
private TextView main_voltage_view;
|
||||
private TextView main_voltage_label;
|
||||
private GoNoGoLights main_lights;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.tab_flight, container, false);
|
||||
|
||||
speed_view = (TextView) v.findViewById(R.id.speed_value);
|
||||
height_view = (TextView) v.findViewById(R.id.height_value);
|
||||
altitude_view = (TextView) v.findViewById(R.id.altitude_value);
|
||||
tilt_view = (View) v.findViewById(R.id.tilt_view);
|
||||
tilt_value = (TextView) v.findViewById(R.id.tilt_value);
|
||||
max_speed_view = (TextView) v.findViewById(R.id.max_speed_value);
|
||||
max_height_view = (TextView) v.findViewById(R.id.max_height_value);
|
||||
max_altitude_view= (TextView) v.findViewById(R.id.max_altitude_value);
|
||||
elevation_view = (TextView) v.findViewById(R.id.elevation_value);
|
||||
range_view = (TextView) v.findViewById(R.id.range_value);
|
||||
bearing_view = (TextView) v.findViewById(R.id.bearing_value);
|
||||
compass_view = (TextView) v.findViewById(R.id.compass_value);
|
||||
distance_view = (TextView) v.findViewById(R.id.distance_value);
|
||||
latitude_view = (TextView) v.findViewById(R.id.lat_value);
|
||||
longitude_view = (TextView) v.findViewById(R.id.lon_value);
|
||||
|
||||
apogee_view = v.findViewById(R.id.apogee_view);
|
||||
apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value);
|
||||
apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
|
||||
(ImageView) v.findViewById(R.id.apogee_greenled),
|
||||
getResources());
|
||||
apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label);
|
||||
|
||||
main_view = v.findViewById(R.id.main_view);
|
||||
main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value);
|
||||
main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
|
||||
(ImageView) v.findViewById(R.id.main_greenled),
|
||||
getResources());
|
||||
main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public String tab_name() { return AltosDroid.tab_flight_name; }
|
||||
|
||||
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
|
||||
if (state != null) {
|
||||
set_value(speed_view, AltosConvert.speed, 1, state.speed());
|
||||
set_value(height_view, AltosConvert.height, 1, state.height());
|
||||
set_value(altitude_view, AltosConvert.height, 1, state.altitude());
|
||||
double orient = state.orient();
|
||||
if (orient == AltosLib.MISSING) {
|
||||
tilt_view.setVisibility(View.GONE);
|
||||
} else {
|
||||
tilt_value.setText(AltosDroid.number("%1.0f°", orient));
|
||||
tilt_view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
set_value(max_speed_view, AltosConvert.speed, 1, state.max_speed());
|
||||
set_value(max_height_view, AltosConvert.height, 1, state.max_height());
|
||||
set_value(max_altitude_view, AltosConvert.height, 1, state.max_altitude());
|
||||
if (from_receiver != null) {
|
||||
elevation_view.setText(AltosDroid.number("%1.0f°", from_receiver.elevation));
|
||||
set_value(range_view, AltosConvert.distance, 1, from_receiver.range);
|
||||
bearing_view.setText(AltosDroid.number("%1.0f°", from_receiver.bearing));
|
||||
compass_view.setText(from_receiver.bearing_words(AltosGreatCircle.BEARING_LONG));
|
||||
set_value(distance_view, AltosConvert.distance, 1, from_receiver.distance);
|
||||
} else {
|
||||
elevation_view.setText("<unknown>");
|
||||
range_view.setText("<unknown>");
|
||||
bearing_view.setText("<unknown>");
|
||||
compass_view.setText("<unknown>");
|
||||
distance_view.setText("<unknown>");
|
||||
}
|
||||
if (state.gps != null) {
|
||||
latitude_view.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
|
||||
longitude_view.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
|
||||
}
|
||||
|
||||
if (state.apogee_voltage == AltosLib.MISSING) {
|
||||
apogee_view.setVisibility(View.GONE);
|
||||
} else {
|
||||
apogee_voltage_view.setText(AltosDroid.number("%1.2f V", state.apogee_voltage));
|
||||
apogee_lights.set(state.apogee_voltage > 3.2, state.apogee_voltage == AltosLib.MISSING);
|
||||
apogee_view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (state.main_voltage == AltosLib.MISSING) {
|
||||
main_view.setVisibility(View.GONE);
|
||||
} else {
|
||||
main_voltage_view.setText(AltosDroid.number("%1.2f V", state.main_voltage));
|
||||
main_lights.set(state.main_voltage > 3.2, state.main_voltage == AltosLib.MISSING);
|
||||
main_view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 org.altusmetrum.altoslib_14.*;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import android.location.Location;
|
||||
|
||||
public class TabMap extends AltosDroidTab implements AltosDroidMapSourceListener {
|
||||
|
||||
AltosLatLon here;
|
||||
|
||||
private TextView mDistanceView;
|
||||
private TextView mBearingLabel;
|
||||
private TextView mBearingView;
|
||||
private TextView mTargetLatitudeView;
|
||||
private TextView mTargetLongitudeView;
|
||||
private TextView mReceiverLatitudeView;
|
||||
private TextView mReceiverLongitudeView;
|
||||
private AltosMapOffline map_offline;
|
||||
private AltosMapOnline map_online;
|
||||
private View view;
|
||||
private int map_source;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
view = inflater.inflate(R.layout.tab_map, container, false);
|
||||
int map_source = AltosDroidPreferences.map_source();
|
||||
|
||||
mDistanceView = (TextView)view.findViewById(R.id.distance_value);
|
||||
mBearingLabel = (TextView)view.findViewById(R.id.bearing_label);
|
||||
mBearingView = (TextView)view.findViewById(R.id.bearing_value);
|
||||
mTargetLatitudeView = (TextView)view.findViewById(R.id.target_lat_value);
|
||||
mTargetLongitudeView = (TextView)view.findViewById(R.id.target_lon_value);
|
||||
mReceiverLatitudeView = (TextView)view.findViewById(R.id.receiver_lat_value);
|
||||
mReceiverLongitudeView = (TextView)view.findViewById(R.id.receiver_lon_value);
|
||||
map_offline = (AltosMapOffline)view.findViewById(R.id.map_offline);
|
||||
map_offline.onCreateView(altos_droid);
|
||||
map_online = new AltosMapOnline(view.getContext());
|
||||
map_online.onCreateView(altos_droid);
|
||||
map_source_changed(AltosDroidPreferences.map_source());
|
||||
AltosDroidPreferences.register_map_source_listener(this);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
if (map_online != null)
|
||||
getChildFragmentManager().beginTransaction().add(R.id.map_online, map_online.mMapFragment).commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
map_offline.onDestroyView();
|
||||
map_online.onDestroyView();
|
||||
AltosDroidPreferences.unregister_map_source_listener(this);
|
||||
}
|
||||
|
||||
public String tab_name() { return AltosDroid.tab_map_name; }
|
||||
|
||||
private void center(double lat, double lon, double accuracy) {
|
||||
if (map_offline != null)
|
||||
map_offline.center(lat, lon, accuracy);
|
||||
if (map_online != null)
|
||||
map_online.center(lat, lon, accuracy);
|
||||
}
|
||||
|
||||
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
|
||||
if (from_receiver != null) {
|
||||
String direction = AltosDroid.direction(from_receiver, receiver);
|
||||
if (direction != null) {
|
||||
mBearingLabel.setText("Direction");
|
||||
mBearingView.setText(direction);
|
||||
} else {
|
||||
mBearingLabel.setText("Bearing");
|
||||
mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
|
||||
}
|
||||
set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
|
||||
} else {
|
||||
mBearingLabel.setText("Bearing");
|
||||
mBearingView.setText("");
|
||||
set_value(mDistanceView, AltosConvert.distance, 6, AltosLib.MISSING);
|
||||
}
|
||||
|
||||
if (state != null) {
|
||||
if (state.gps != null) {
|
||||
mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
|
||||
mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
|
||||
}
|
||||
}
|
||||
|
||||
if (receiver != null) {
|
||||
double accuracy;
|
||||
|
||||
here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
|
||||
if (receiver.hasAccuracy())
|
||||
accuracy = receiver.getAccuracy();
|
||||
else
|
||||
accuracy = 1000;
|
||||
mReceiverLatitudeView.setText(AltosDroid.pos(here.lat, "N", "S"));
|
||||
mReceiverLongitudeView.setText(AltosDroid.pos(here.lon, "E", "W"));
|
||||
center (here.lat, here.lon, accuracy);
|
||||
}
|
||||
if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) {
|
||||
if (map_offline != null)
|
||||
map_offline.show(telem_state, state, from_receiver, receiver);
|
||||
} else {
|
||||
if (map_online != null)
|
||||
map_online.show(telem_state, state, from_receiver, receiver);
|
||||
}
|
||||
}
|
||||
|
||||
public void map_source_changed(int map_source) {
|
||||
this.map_source = map_source;
|
||||
if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) {
|
||||
if (map_online != null)
|
||||
map_online.set_visible(false);
|
||||
if (map_offline != null) {
|
||||
map_offline.set_visible(true);
|
||||
map_offline.show(last_telem_state, last_state, last_from_receiver, last_receiver);
|
||||
}
|
||||
} else {
|
||||
if (map_offline != null)
|
||||
map_offline.set_visible(false);
|
||||
if (map_online != null) {
|
||||
map_online.set_visible(true);
|
||||
map_online.show(last_telem_state, last_state, last_from_receiver, last_receiver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TabMap() {
|
||||
}
|
||||
}
|
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 org.altusmetrum.altoslib_14.*;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import android.location.Location;
|
||||
|
||||
public class TabPad extends AltosDroidTab {
|
||||
private TextView battery_voltage_view;
|
||||
private GoNoGoLights battery_lights;
|
||||
|
||||
private TableRow receiver_row;
|
||||
private TextView receiver_voltage_view;
|
||||
private TextView receiver_voltage_label;
|
||||
private GoNoGoLights receiver_voltage_lights;
|
||||
|
||||
private TableRow apogee_row;
|
||||
private TextView apogee_voltage_view;
|
||||
private TextView apogee_voltage_label;
|
||||
private GoNoGoLights apogee_lights;
|
||||
|
||||
private TableRow main_row;
|
||||
private TextView main_voltage_view;
|
||||
private TextView main_voltage_label;
|
||||
private GoNoGoLights main_lights;
|
||||
|
||||
private TextView data_logging_view;
|
||||
private GoNoGoLights data_logging_lights;
|
||||
|
||||
private TextView gps_locked_view;
|
||||
private GoNoGoLights gps_locked_lights;
|
||||
|
||||
private TextView gps_ready_view;
|
||||
private GoNoGoLights gps_ready_lights;
|
||||
|
||||
private TextView receiver_latitude_view;
|
||||
private TextView receiver_longitude_view;
|
||||
private TextView receiver_altitude_view;
|
||||
|
||||
private TableRow[] ignite_row = new TableRow[4];
|
||||
private TextView[] ignite_voltage_view = new TextView[4];
|
||||
private TextView[] ignite_voltage_label = new TextView[4];
|
||||
private GoNoGoLights[] ignite_lights = new GoNoGoLights[4];
|
||||
|
||||
private View tilt_view;
|
||||
private TextView tilt_value;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
AltosDebug.debug("TabPad onCreateView\n");
|
||||
View v = inflater.inflate(R.layout.tab_pad, container, false);
|
||||
battery_voltage_view = (TextView) v.findViewById(R.id.battery_voltage_value);
|
||||
battery_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled),
|
||||
(ImageView) v.findViewById(R.id.battery_greenled),
|
||||
getResources());
|
||||
|
||||
receiver_row = (TableRow) v.findViewById(R.id.receiver_row);
|
||||
receiver_voltage_view = (TextView) v.findViewById(R.id.receiver_voltage_value);
|
||||
receiver_voltage_label = (TextView) v.findViewById(R.id.receiver_voltage_label);
|
||||
receiver_voltage_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.receiver_redled),
|
||||
(ImageView) v.findViewById(R.id.receiver_greenled),
|
||||
getResources());
|
||||
|
||||
apogee_row = (TableRow) v.findViewById(R.id.apogee_row);
|
||||
apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value);
|
||||
apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label);
|
||||
apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
|
||||
(ImageView) v.findViewById(R.id.apogee_greenled),
|
||||
getResources());
|
||||
|
||||
main_row = (TableRow) v.findViewById(R.id.main_row);
|
||||
main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value);
|
||||
main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label);
|
||||
main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
|
||||
(ImageView) v.findViewById(R.id.main_greenled),
|
||||
getResources());
|
||||
|
||||
data_logging_view = (TextView) v.findViewById(R.id.logging_value);
|
||||
data_logging_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.logging_redled),
|
||||
(ImageView) v.findViewById(R.id.logging_greenled),
|
||||
getResources());
|
||||
|
||||
gps_locked_view = (TextView) v.findViewById(R.id.gps_locked_value);
|
||||
gps_locked_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_locked_redled),
|
||||
(ImageView) v.findViewById(R.id.gps_locked_greenled),
|
||||
getResources());
|
||||
|
||||
gps_ready_view = (TextView) v.findViewById(R.id.gps_ready_value);
|
||||
gps_ready_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_ready_redled),
|
||||
(ImageView) v.findViewById(R.id.gps_ready_greenled),
|
||||
getResources());
|
||||
|
||||
tilt_view = (View) v.findViewById(R.id.tilt_view);
|
||||
tilt_value = (TextView) v.findViewById(R.id.tilt_value);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int row_id, view_id, label_id, lights_id;
|
||||
int red_id, green_id;
|
||||
switch (i) {
|
||||
case 0:
|
||||
default:
|
||||
row_id = R.id.ignite_a_row;
|
||||
view_id = R.id.ignite_a_voltage_value;
|
||||
label_id = R.id.ignite_a_voltage_label;
|
||||
red_id = R.id.ignite_a_redled;
|
||||
green_id = R.id.ignite_a_greenled;
|
||||
break;
|
||||
case 1:
|
||||
row_id = R.id.ignite_b_row;
|
||||
view_id = R.id.ignite_b_voltage_value;
|
||||
label_id = R.id.ignite_b_voltage_label;
|
||||
red_id = R.id.ignite_b_redled;
|
||||
green_id = R.id.ignite_b_greenled;
|
||||
break;
|
||||
case 2:
|
||||
row_id = R.id.ignite_c_row;
|
||||
view_id = R.id.ignite_c_voltage_value;
|
||||
label_id = R.id.ignite_c_voltage_label;
|
||||
red_id = R.id.ignite_c_redled;
|
||||
green_id = R.id.ignite_c_greenled;
|
||||
break;
|
||||
case 3:
|
||||
row_id = R.id.ignite_d_row;
|
||||
view_id = R.id.ignite_d_voltage_value;
|
||||
label_id = R.id.ignite_d_voltage_label;
|
||||
red_id = R.id.ignite_d_redled;
|
||||
green_id = R.id.ignite_d_greenled;
|
||||
break;
|
||||
}
|
||||
ignite_row[i] = (TableRow) v.findViewById(row_id);
|
||||
ignite_voltage_view[i] = (TextView) v.findViewById(view_id);
|
||||
ignite_voltage_label[i] = (TextView) v.findViewById(label_id);
|
||||
ignite_lights[i] = new GoNoGoLights((ImageView) v.findViewById(red_id),
|
||||
(ImageView) v.findViewById(green_id),
|
||||
getResources());
|
||||
}
|
||||
|
||||
receiver_latitude_view = (TextView) v.findViewById(R.id.receiver_lat_value);
|
||||
receiver_longitude_view = (TextView) v.findViewById(R.id.receiver_lon_value);
|
||||
receiver_altitude_view = (TextView) v.findViewById(R.id.receiver_alt_value);
|
||||
|
||||
AltosDebug.debug("TabPad onCreateView done battery_voltage_view %s\n", battery_voltage_view);
|
||||
return v;
|
||||
}
|
||||
|
||||
public String tab_name() { return AltosDroid.tab_pad_name; }
|
||||
|
||||
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
|
||||
AltosDebug.debug("pad show state %b bvv %s\n", state != null, battery_voltage_view);
|
||||
if (state != null) {
|
||||
battery_voltage_view.setText(AltosDroid.number("%1.2f V", state.battery_voltage));
|
||||
battery_lights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING);
|
||||
if (state.apogee_voltage == AltosLib.MISSING) {
|
||||
apogee_row.setVisibility(View.GONE);
|
||||
} else {
|
||||
apogee_voltage_view.setText(AltosDroid.number("%1.2f V", state.apogee_voltage));
|
||||
apogee_row.setVisibility(View.VISIBLE);
|
||||
}
|
||||
apogee_lights.set(state.apogee_voltage >= AltosLib.ao_igniter_good, state.apogee_voltage == AltosLib.MISSING);
|
||||
if (state.main_voltage == AltosLib.MISSING) {
|
||||
main_row.setVisibility(View.GONE);
|
||||
} else {
|
||||
main_voltage_view.setText(AltosDroid.number("%1.2f V", state.main_voltage));
|
||||
main_row.setVisibility(View.VISIBLE);
|
||||
}
|
||||
main_lights.set(state.main_voltage >= AltosLib.ao_igniter_good, state.main_voltage == AltosLib.MISSING);
|
||||
|
||||
int num_igniter = state.igniter_voltage == null ? 0 : state.igniter_voltage.length;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
double voltage = i >= num_igniter ? AltosLib.MISSING : state.igniter_voltage[i];
|
||||
if (voltage == AltosLib.MISSING) {
|
||||
ignite_row[i].setVisibility(View.GONE);
|
||||
} else {
|
||||
ignite_voltage_view[i].setText(AltosDroid.number("%1.2f V", voltage));
|
||||
ignite_row[i].setVisibility(View.VISIBLE);
|
||||
}
|
||||
ignite_lights[i].set(voltage >= AltosLib.ao_igniter_good, voltage == AltosLib.MISSING);
|
||||
}
|
||||
|
||||
if (state.cal_data().flight != 0) {
|
||||
if (state.state() <= AltosLib.ao_flight_pad)
|
||||
data_logging_view.setText("Ready to record");
|
||||
else if (state.state() < AltosLib.ao_flight_landed)
|
||||
data_logging_view.setText("Recording data");
|
||||
else
|
||||
data_logging_view.setText("Recorded data");
|
||||
} else {
|
||||
data_logging_view.setText("Storage full");
|
||||
}
|
||||
data_logging_lights.set(state.cal_data().flight != 0, state.cal_data().flight == AltosLib.MISSING);
|
||||
|
||||
if (state.gps != null) {
|
||||
int soln = state.gps.nsat;
|
||||
int nsat = state.gps.cc_gps_sat != null ? state.gps.cc_gps_sat.length : 0;
|
||||
gps_locked_view.setText(String.format("%d in soln, %d in view", soln, nsat));
|
||||
gps_locked_lights.set(state.gps.locked && state.gps.nsat >= 4, false);
|
||||
if (state.gps_ready)
|
||||
gps_ready_view.setText("Ready");
|
||||
else
|
||||
gps_ready_view.setText(AltosDroid.integer("Waiting %d", state.gps_waiting));
|
||||
} else
|
||||
gps_locked_lights.set(false, true);
|
||||
gps_ready_lights.set(state.gps_ready, state.gps == null);
|
||||
|
||||
double orient = state.orient();
|
||||
|
||||
if (orient == AltosLib.MISSING) {
|
||||
tilt_view.setVisibility(View.GONE);
|
||||
} else {
|
||||
tilt_value.setText(AltosDroid.number("%1.0f°", orient));
|
||||
tilt_view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (telem_state != null) {
|
||||
if (telem_state.receiver_battery == AltosLib.MISSING) {
|
||||
receiver_row.setVisibility(View.GONE);
|
||||
} else {
|
||||
receiver_voltage_view.setText(AltosDroid.number("%1.2f V", telem_state.receiver_battery));
|
||||
receiver_row.setVisibility(View.VISIBLE);
|
||||
}
|
||||
receiver_voltage_lights.set(telem_state.receiver_battery >= AltosLib.ao_battery_good, telem_state.receiver_battery == AltosLib.MISSING);
|
||||
}
|
||||
|
||||
if (receiver != null) {
|
||||
double altitude = AltosLib.MISSING;
|
||||
if (receiver.hasAltitude())
|
||||
altitude = receiver.getAltitude();
|
||||
String lat_text = AltosDroid.pos(receiver.getLatitude(), "N", "S");
|
||||
String lon_text = AltosDroid.pos(receiver.getLongitude(), "E", "W");
|
||||
AltosDebug.debug("lat %s lon %s\n", lat_text, lon_text);
|
||||
receiver_latitude_view.setText(lat_text);
|
||||
receiver_longitude_view.setText(lon_text);
|
||||
set_value(receiver_altitude_view, AltosConvert.height, 1, altitude);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 org.altusmetrum.altoslib_14.*;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.location.Location;
|
||||
|
||||
public class TabRecover extends AltosDroidTab {
|
||||
private TextView mBearingView;
|
||||
private TextView mDirectionView;
|
||||
private TextView mDistanceView;
|
||||
private TextView mTargetLatitudeView;
|
||||
private TextView mTargetLongitudeView;
|
||||
private TextView mReceiverLatitudeView;
|
||||
private TextView mReceiverLongitudeView;
|
||||
private TextView mMaxHeightView;
|
||||
private TextView mMaxSpeedView;
|
||||
private TextView mMaxAccelView;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.tab_recover, container, false);
|
||||
|
||||
mBearingView = (TextView) v.findViewById(R.id.bearing_value);
|
||||
mDirectionView = (TextView) v.findViewById(R.id.direction_value);
|
||||
mDistanceView = (TextView) v.findViewById(R.id.distance_value);
|
||||
mTargetLatitudeView = (TextView) v.findViewById(R.id.target_lat_value);
|
||||
mTargetLongitudeView = (TextView) v.findViewById(R.id.target_lon_value);
|
||||
mReceiverLatitudeView = (TextView) v.findViewById(R.id.receiver_lat_value);
|
||||
mReceiverLongitudeView = (TextView) v.findViewById(R.id.receiver_lon_value);
|
||||
mMaxHeightView = (TextView) v.findViewById(R.id.max_height_value);
|
||||
mMaxSpeedView = (TextView) v.findViewById(R.id.max_speed_value);
|
||||
mMaxAccelView = (TextView) v.findViewById(R.id.max_accel_value);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public String tab_name() { return AltosDroid.tab_recover_name; }
|
||||
|
||||
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
|
||||
if (from_receiver != null) {
|
||||
mBearingView.setText(String.format("%1.0f°", from_receiver.bearing));
|
||||
set_value(mDistanceView, AltosConvert.distance, 1, from_receiver.distance);
|
||||
String direction = AltosDroid.direction(from_receiver, receiver);
|
||||
if (direction == null)
|
||||
mDirectionView.setText("");
|
||||
else
|
||||
mDirectionView.setText(direction);
|
||||
}
|
||||
if (state != null && state.gps != null) {
|
||||
mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
|
||||
mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
|
||||
}
|
||||
|
||||
if (receiver != null) {
|
||||
mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
|
||||
mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
|
||||
}
|
||||
|
||||
if (state != null) {
|
||||
set_value(mMaxHeightView, AltosConvert.height, 1, state.max_height());
|
||||
set_value(mMaxAccelView, AltosConvert.accel, 1, state.max_acceleration());
|
||||
set_value(mMaxSpeedView, AltosConvert.speed, 1, state.max_speed());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.AltosDroid;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TabHost;
|
||||
import android.widget.TabWidget;
|
||||
|
||||
/**
|
||||
* This is a helper class that implements the management of tabs and all
|
||||
* details of connecting a ViewPager with associated TabHost. It relies on a
|
||||
* trick. Normally a tab host has a simple API for supplying a View or
|
||||
* Intent that each tab will show. This is not sufficient for switching
|
||||
* between pages. So instead we make the content part of the tab host
|
||||
* 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
|
||||
* view to show as the tab content. It listens to changes in tabs, and takes
|
||||
* care of switch to the correct paged in the ViewPager whenever the selected
|
||||
* tab changes.
|
||||
*/
|
||||
public class TabsAdapter extends FragmentPagerAdapter
|
||||
implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
|
||||
private final Context mContext;
|
||||
private final TabHost mTabHost;
|
||||
private final ViewPager mViewPager;
|
||||
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
|
||||
private int position;
|
||||
|
||||
static class TabInfo {
|
||||
private final String tag;
|
||||
private final Class<?> clss;
|
||||
private final Bundle args;
|
||||
private Fragment fragment;
|
||||
|
||||
TabInfo(String _tag, Class<?> _class, Bundle _args, Fragment _fragment) {
|
||||
tag = _tag;
|
||||
clss = _class;
|
||||
args = _args;
|
||||
fragment = _fragment;
|
||||
}
|
||||
}
|
||||
|
||||
static class DummyTabFactory implements TabHost.TabContentFactory {
|
||||
private final Context mContext;
|
||||
|
||||
public DummyTabFactory(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public View createTabContent(String tag) {
|
||||
View v = new View(mContext);
|
||||
v.setMinimumWidth(0);
|
||||
v.setMinimumHeight(0);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
|
||||
super(activity.getSupportFragmentManager());
|
||||
mContext = activity;
|
||||
mTabHost = tabHost;
|
||||
mViewPager = pager;
|
||||
mTabHost.setOnTabChangedListener(this);
|
||||
mViewPager.setAdapter(this);
|
||||
mViewPager.addOnPageChangeListener(this);
|
||||
}
|
||||
|
||||
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args, Fragment fragment) {
|
||||
tabSpec.setContent(new DummyTabFactory(mContext));
|
||||
String tag = tabSpec.getTag();
|
||||
|
||||
TabInfo info = new TabInfo(tag, clss, args, fragment);
|
||||
mTabs.add(info);
|
||||
mTabHost.addTab(tabSpec);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mTabs.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
TabInfo info = mTabs.get(position);
|
||||
AltosDebug.debug("TabsAdapter.getItem(%d)", position);
|
||||
if (info.fragment == null)
|
||||
info.fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
|
||||
return info.fragment;
|
||||
}
|
||||
|
||||
public Fragment currentItem() {
|
||||
TabInfo info = mTabs.get(position);
|
||||
return info.fragment;
|
||||
}
|
||||
|
||||
public void onTabChanged(String tabId) {
|
||||
AltosDroidTab prev_frag = (AltosDroidTab) mTabs.get(position).fragment;
|
||||
|
||||
position = mTabHost.getCurrentTab();
|
||||
|
||||
AltosDroidTab cur_frag = (AltosDroidTab) mTabs.get(position).fragment;
|
||||
|
||||
AltosDebug.debug("TabsAdapter.onTabChanged(%s) = %d cur %s prev %s", tabId, position, cur_frag, prev_frag);
|
||||
|
||||
if (prev_frag != cur_frag) {
|
||||
if (prev_frag != null) {
|
||||
prev_frag.set_visible(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* This happens when the tab is selected before any of them
|
||||
* have been created, like during rotation
|
||||
*/
|
||||
if (cur_frag != null)
|
||||
cur_frag.set_visible(true);
|
||||
mViewPager.setCurrentItem(position);
|
||||
}
|
||||
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
}
|
||||
|
||||
public void onPageSelected(int position) {
|
||||
// Unfortunately when TabHost changes the current tab, it kindly
|
||||
// also takes care of putting focus on it when not in touch mode.
|
||||
// The jerk.
|
||||
// This hack tries to prevent this from pulling focus out of our
|
||||
// ViewPager.
|
||||
TabWidget widget = mTabHost.getTabWidget();
|
||||
int oldFocusability = widget.getDescendantFocusability();
|
||||
widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
||||
mTabHost.setCurrentTab(position);
|
||||
widget.setDescendantFocusability(oldFocusability);
|
||||
}
|
||||
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
}
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright © 2021 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, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.io.*;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Environment;
|
||||
|
||||
public class TelemetryLogger implements AltosLogTrace {
|
||||
private TelemetryService service = null;
|
||||
private AltosLink link = null;
|
||||
private AltosLog logger = null;
|
||||
|
||||
private BroadcastReceiver mExternalStorageReceiver;
|
||||
|
||||
/* AltosLogTrace interface */
|
||||
public void trace(String format, Object ... arguments) {
|
||||
AltosDebug.debug(format, arguments);
|
||||
}
|
||||
|
||||
public void open_failed(File file) {
|
||||
service.send_file_failed_to_clients(file);
|
||||
}
|
||||
|
||||
public TelemetryLogger(TelemetryService in_service, AltosLink in_link) {
|
||||
service = in_service;
|
||||
link = in_link;
|
||||
|
||||
startWatchingExternalStorage();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
stopWatchingExternalStorage();
|
||||
close();
|
||||
}
|
||||
|
||||
private void close() {
|
||||
if (logger != null) {
|
||||
AltosDebug.debug("Shutting down Telemetry Logging");
|
||||
logger.close();
|
||||
logger = null;
|
||||
}
|
||||
}
|
||||
|
||||
void handleExternalStorageState() {
|
||||
String state = Environment.getExternalStorageState();
|
||||
if (Environment.MEDIA_MOUNTED.equals(state)) {
|
||||
if (logger == null) {
|
||||
AltosDebug.debug("Starting up Telemetry Logging");
|
||||
logger = new AltosLog(link,this);
|
||||
}
|
||||
} else {
|
||||
AltosDebug.debug("External Storage not present - stopping");
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void startWatchingExternalStorage() {
|
||||
mExternalStorageReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
handleExternalStorageState();
|
||||
}
|
||||
};
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
|
||||
filter.addAction(Intent.ACTION_MEDIA_REMOVED);
|
||||
service.registerReceiver(mExternalStorageReceiver, filter);
|
||||
handleExternalStorageState();
|
||||
}
|
||||
|
||||
void stopWatchingExternalStorage() {
|
||||
service.unregisterReceiver(mExternalStorageReceiver);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright © 2011 Keith Packard <keithp@keithp.com>
|
||||
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.text.*;
|
||||
import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
import android.os.Handler;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
|
||||
public class TelemetryReader extends Thread {
|
||||
|
||||
int crc_errors;
|
||||
|
||||
Handler handler;
|
||||
|
||||
AltosLink link;
|
||||
|
||||
LinkedBlockingQueue<AltosLine> telemQueue;
|
||||
|
||||
public AltosTelemetry read() throws ParseException, AltosCRCException, InterruptedException, IOException {
|
||||
AltosLine l = telemQueue.take();
|
||||
if (l.line == null)
|
||||
throw new IOException("IO error");
|
||||
AltosTelemetry telem = AltosTelemetryLegacy.parse(l.line);
|
||||
return telem;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
link.remove_monitor(telemQueue);
|
||||
link = null;
|
||||
telemQueue.clear();
|
||||
telemQueue = null;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
AltosDebug.debug("starting loop");
|
||||
while (telemQueue != null) {
|
||||
try {
|
||||
AltosTelemetry telem = read();
|
||||
telem.set_frequency(link.frequency);
|
||||
handler.obtainMessage(TelemetryService.MSG_TELEMETRY, telem).sendToTarget();
|
||||
} catch (ParseException pp) {
|
||||
AltosDebug.error("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage());
|
||||
} catch (AltosCRCException ce) {
|
||||
++crc_errors;
|
||||
handler.obtainMessage(TelemetryService.MSG_CRC_ERROR, new Integer(crc_errors)).sendToTarget();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ee) {
|
||||
} catch (IOException ie) {
|
||||
AltosDebug.error("IO exception in telemetry reader");
|
||||
handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, link).sendToTarget();
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
public TelemetryReader (AltosLink in_link, Handler in_handler) {
|
||||
AltosDebug.debug("connected TelemetryReader create started");
|
||||
link = in_link;
|
||||
handler = in_handler;
|
||||
|
||||
telemQueue = new LinkedBlockingQueue<AltosLine>();
|
||||
link.add_monitor(telemQueue);
|
||||
link.set_telemetry(AltosLib.ao_telemetry_standard);
|
||||
|
||||
AltosDebug.debug("connected TelemetryReader created");
|
||||
}
|
||||
}
|
@@ -0,0 +1,748 @@
|
||||
/*
|
||||
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.concurrent.TimeoutException;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import android.app.*;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.graphics.Color;
|
||||
import android.hardware.usb.*;
|
||||
import android.content.Intent;
|
||||
import android.content.Context;
|
||||
import android.os.*;
|
||||
import android.os.Build.*;
|
||||
import android.widget.Toast;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class TelemetryService extends Service implements AltosIdleMonitorListener {
|
||||
|
||||
static final int MSG_REGISTER_CLIENT = 1;
|
||||
static final int MSG_UNREGISTER_CLIENT = 2;
|
||||
static final int MSG_CONNECT = 3;
|
||||
static final int MSG_OPEN_USB = 4;
|
||||
static final int MSG_CONNECTED = 5;
|
||||
static final int MSG_CONNECT_FAILED = 6;
|
||||
static final int MSG_DISCONNECTED = 7;
|
||||
static final int MSG_TELEMETRY = 8;
|
||||
static final int MSG_SETFREQUENCY = 9;
|
||||
static final int MSG_CRC_ERROR = 10;
|
||||
static final int MSG_SETBAUD = 11;
|
||||
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.
|
||||
private int NOTIFICATION = R.string.telemetry_service_label;
|
||||
//private NotificationManager mNM;
|
||||
|
||||
ArrayList<Messenger> clients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
|
||||
final Handler handler = new IncomingHandler(this);
|
||||
final Messenger messenger = new Messenger(handler); // Target we publish for clients to send messages to IncomingHandler.
|
||||
|
||||
// Name of the connected device
|
||||
DeviceAddress address;
|
||||
private AltosDroidLink altos_link = null;
|
||||
private TelemetryReader telemetry_reader = null;
|
||||
private TelemetryLogger telemetry_logger = null;
|
||||
|
||||
// Local Bluetooth adapter
|
||||
private BluetoothAdapter bluetooth_adapter = null;
|
||||
|
||||
// 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;
|
||||
IncomingHandler(TelemetryService s) { service = new WeakReference<TelemetryService>(s); }
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
DeviceAddress address;
|
||||
|
||||
TelemetryService s = service.get();
|
||||
AltosDroidLink bt = null;
|
||||
if (s == null)
|
||||
return;
|
||||
|
||||
switch (msg.what) {
|
||||
|
||||
/* Messages from application */
|
||||
case MSG_REGISTER_CLIENT:
|
||||
s.add_client(msg.replyTo);
|
||||
break;
|
||||
case MSG_UNREGISTER_CLIENT:
|
||||
s.remove_client(msg.replyTo);
|
||||
break;
|
||||
case MSG_CONNECT:
|
||||
AltosDebug.debug("Connect command received");
|
||||
address = (DeviceAddress) msg.obj;
|
||||
AltosDroidPreferences.set_active_device(address);
|
||||
s.start_altos_bluetooth(address, false);
|
||||
break;
|
||||
case MSG_OPEN_USB:
|
||||
AltosDebug.debug("Open USB command received");
|
||||
UsbDevice device = (UsbDevice) msg.obj;
|
||||
s.start_usb(device);
|
||||
break;
|
||||
case MSG_DISCONNECT:
|
||||
AltosDebug.debug("Disconnect command received");
|
||||
s.address = null;
|
||||
if (!(Boolean) msg.obj)
|
||||
AltosDroidPreferences.set_active_device(null);
|
||||
s.disconnect(true);
|
||||
break;
|
||||
case MSG_DELETE_SERIAL:
|
||||
AltosDebug.debug("Delete Serial command received");
|
||||
s.delete_serial((Integer) msg.obj);
|
||||
break;
|
||||
case MSG_SETFREQUENCY:
|
||||
AltosDebug.debug("MSG_SETFREQUENCY");
|
||||
s.telemetry_state.frequency = (Double) msg.obj;
|
||||
if (s.idle_monitor != null) {
|
||||
s.idle_monitor.set_frequency(s.telemetry_state.frequency);
|
||||
} else if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
|
||||
try {
|
||||
s.altos_link.set_radio_frequency(s.telemetry_state.frequency);
|
||||
s.altos_link.save_frequency();
|
||||
} catch (InterruptedException e) {
|
||||
} catch (TimeoutException e) {
|
||||
}
|
||||
}
|
||||
s.send_to_clients();
|
||||
break;
|
||||
case MSG_SETBAUD:
|
||||
AltosDebug.debug("MSG_SETBAUD");
|
||||
s.telemetry_state.telemetry_rate = (Integer) msg.obj;
|
||||
if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
|
||||
s.altos_link.set_telemetry_rate(s.telemetry_state.telemetry_rate);
|
||||
s.altos_link.save_telemetry_rate();
|
||||
}
|
||||
s.send_to_clients();
|
||||
break;
|
||||
|
||||
/*
|
||||
*Messages from AltosBluetooth
|
||||
*/
|
||||
case MSG_CONNECTED:
|
||||
AltosDebug.debug("MSG_CONNECTED");
|
||||
bt = (AltosDroidLink) msg.obj;
|
||||
|
||||
if (bt != s.altos_link) {
|
||||
AltosDebug.debug("Stale message");
|
||||
break;
|
||||
}
|
||||
AltosDebug.debug("Connected to device");
|
||||
try {
|
||||
s.connected();
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
break;
|
||||
case MSG_CONNECT_FAILED:
|
||||
AltosDebug.debug("MSG_CONNECT_FAILED");
|
||||
bt = (AltosDroidLink) msg.obj;
|
||||
|
||||
if (bt != s.altos_link) {
|
||||
AltosDebug.debug("Stale message");
|
||||
break;
|
||||
}
|
||||
if (s.address != null) {
|
||||
AltosDebug.debug("Connection failed... retrying");
|
||||
s.start_altos_bluetooth(s.address, true);
|
||||
} else {
|
||||
s.disconnect(true);
|
||||
}
|
||||
break;
|
||||
case MSG_DISCONNECTED:
|
||||
|
||||
/* This can be sent by either AltosDroidLink or TelemetryReader */
|
||||
AltosDebug.debug("MSG_DISCONNECTED");
|
||||
bt = (AltosDroidLink) msg.obj;
|
||||
|
||||
if (bt != s.altos_link) {
|
||||
AltosDebug.debug("Stale message");
|
||||
break;
|
||||
}
|
||||
if (s.address != null) {
|
||||
AltosDebug.debug("Connection lost... retrying");
|
||||
s.start_altos_bluetooth(s.address, true);
|
||||
} else {
|
||||
s.disconnect(true);
|
||||
}
|
||||
break;
|
||||
|
||||
/*
|
||||
* Messages from TelemetryReader
|
||||
*/
|
||||
case MSG_TELEMETRY:
|
||||
s.telemetry((AltosTelemetry) msg.obj);
|
||||
break;
|
||||
case MSG_CRC_ERROR:
|
||||
// forward crc error messages
|
||||
s.telemetry_state.crc_errors = (Integer) msg.obj;
|
||||
s.send_to_clients();
|
||||
break;
|
||||
case MSG_BLUETOOTH_ENABLED:
|
||||
AltosDebug.debug("TelemetryService notes that BT is now enabled");
|
||||
address = AltosDroidPreferences.active_device();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle telemetry packet
|
||||
*/
|
||||
private void telemetry(AltosTelemetry telem) {
|
||||
AltosState state;
|
||||
|
||||
state = telemetry_state.get(telem.serial());
|
||||
if (state == null)
|
||||
state = new AltosState(new AltosCalData());
|
||||
telem.provide_data(state);
|
||||
telemetry_state.put(telem.serial(), state);
|
||||
telemetry_state.quiet = false;
|
||||
if (state != null) {
|
||||
AltosPreferences.set_state(state,telem.serial());
|
||||
}
|
||||
send_to_clients();
|
||||
}
|
||||
|
||||
/* Construct the message to deliver to clients
|
||||
*/
|
||||
private Message message() {
|
||||
if (telemetry_state == null)
|
||||
AltosDebug.debug("telemetry_state null!");
|
||||
return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state);
|
||||
}
|
||||
|
||||
/* A new friend has connected
|
||||
*/
|
||||
private void add_client(Messenger client) {
|
||||
|
||||
clients.add(client);
|
||||
AltosDebug.debug("Client bound to 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
|
||||
*/
|
||||
if (address != null && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) {
|
||||
AltosDebug.debug("Reconnecting now...");
|
||||
start_altos_bluetooth(address, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* A client has disconnected, clean up
|
||||
*/
|
||||
private void remove_client(Messenger client) {
|
||||
clients.remove(client);
|
||||
AltosDebug.debug("Client unbound from service");
|
||||
|
||||
/* When the list of clients is empty, stop the service if
|
||||
* we have no current telemetry source
|
||||
*/
|
||||
|
||||
if (clients.isEmpty() && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) {
|
||||
AltosDebug.debug("No clients, no connection. Stopping\n");
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
|
||||
private void send_to_client(Messenger client) {
|
||||
Message m = message();
|
||||
try {
|
||||
client.send(m);
|
||||
} catch (RemoteException e) {
|
||||
AltosDebug.error("Client %s disappeared", client.toString());
|
||||
remove_client(client);
|
||||
}
|
||||
}
|
||||
|
||||
private void send_to_clients() {
|
||||
for (Messenger client : clients)
|
||||
send_to_client(client);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private void send_idle_mode_to_clients() {
|
||||
for (Messenger client : clients)
|
||||
send_idle_mode_to_client(client);
|
||||
}
|
||||
|
||||
private void send_file_failed_to_client(Messenger client, File f) {
|
||||
Message m = Message.obtain(null, AltosDroid.MSG_FILE_FAILED, f);
|
||||
try {
|
||||
client.send(m);
|
||||
} catch (RemoteException e) {
|
||||
AltosDebug.error("Client %s disappeared", client.toString());
|
||||
remove_client(client);
|
||||
}
|
||||
}
|
||||
|
||||
public void send_file_failed_to_clients(File f) {
|
||||
for (Messenger client : clients)
|
||||
send_file_failed_to_client(client, f);
|
||||
}
|
||||
|
||||
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();
|
||||
try {
|
||||
telemetry_reader.join();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
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();
|
||||
telemetry_logger = null;
|
||||
}
|
||||
if (altos_link != null) {
|
||||
AltosDebug.debug("disconnect(): stopping AltosDroidLink");
|
||||
altos_link.close();
|
||||
altos_link = null;
|
||||
ignite = null;
|
||||
}
|
||||
telemetry_state.config = null;
|
||||
if (notify) {
|
||||
AltosDebug.debug("disconnect(): send message to clients");
|
||||
send_to_clients();
|
||||
if (clients.isEmpty()) {
|
||||
AltosDebug.debug("disconnect(): no clients, terminating");
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void start_usb(UsbDevice device) {
|
||||
AltosUsb d = new AltosUsb(this, device, handler);
|
||||
|
||||
if (d != null) {
|
||||
disconnect(false);
|
||||
altos_link = d;
|
||||
try {
|
||||
connected();
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void delete_serial(int serial) {
|
||||
telemetry_state.remove(serial);
|
||||
AltosPreferences.remove_state(serial);
|
||||
send_to_clients();
|
||||
}
|
||||
|
||||
private void start_altos_bluetooth(DeviceAddress address, boolean pause) {
|
||||
if (bluetooth_adapter == null || !bluetooth_adapter.isEnabled() || address.address == null)
|
||||
return;
|
||||
|
||||
disconnect(false);
|
||||
|
||||
// Get the BluetoothDevice object
|
||||
BluetoothDevice device = bluetooth_adapter.getRemoteDevice(address.address);
|
||||
|
||||
this.address = address;
|
||||
AltosDebug.debug("start_altos_bluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress());
|
||||
altos_link = new AltosBluetooth(device, handler, pause);
|
||||
telemetry_state.connect = TelemetryState.CONNECT_CONNECTING;
|
||||
telemetry_state.address = address;
|
||||
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.set_frequency(telemetry_state.frequency);
|
||||
telemetry_state.idle_mode = true;
|
||||
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_state.idle_mode = false;
|
||||
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 && idle_monitor == null && !ignite_running) {
|
||||
try {
|
||||
double voltage = altos_link.monitor_battery();
|
||||
telemetry_state.receiver_battery = voltage;
|
||||
send_to_clients();
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void stop_receiver_voltage_timer() {
|
||||
if (receiver_voltage_timer != null) {
|
||||
receiver_voltage_timer.cancel();
|
||||
receiver_voltage_timer.purge();
|
||||
receiver_voltage_timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void start_receiver_voltage_timer() {
|
||||
if (receiver_voltage_timer == null && altos_link.has_monitor_battery()) {
|
||||
receiver_voltage_timer = new Timer();
|
||||
receiver_voltage_timer.scheduleAtFixedRate(new TimerTask() { public void run() {update_receiver_voltage();}}, 1000L, 10000L);
|
||||
}
|
||||
}
|
||||
|
||||
private void connected() throws InterruptedException {
|
||||
AltosDebug.debug("connected top");
|
||||
AltosDebug.check_ui("connected\n");
|
||||
try {
|
||||
if (altos_link == null)
|
||||
throw new InterruptedException("no bluetooth");
|
||||
telemetry_state.config = altos_link.config_data();
|
||||
altos_link.set_radio_frequency(telemetry_state.frequency);
|
||||
altos_link.set_telemetry_rate(telemetry_state.telemetry_rate);
|
||||
} catch (TimeoutException e) {
|
||||
// If this timed out, then we really want to retry it, but
|
||||
// probably safer to just retry the connection from scratch.
|
||||
AltosDebug.debug("connected timeout");
|
||||
if (address != null) {
|
||||
AltosDebug.debug("connected timeout, retrying");
|
||||
start_altos_bluetooth(address, true);
|
||||
} else {
|
||||
handler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget();
|
||||
disconnect(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
AltosDebug.debug("connected bluetooth configured");
|
||||
telemetry_state.connect = TelemetryState.CONNECT_CONNECTED;
|
||||
telemetry_state.address = address;
|
||||
|
||||
telemetry_start();
|
||||
|
||||
AltosDebug.debug("connected TelemetryReader started");
|
||||
|
||||
telemetry_logger = new TelemetryLogger(this, altos_link);
|
||||
|
||||
start_receiver_voltage_timer();
|
||||
|
||||
AltosDebug.debug("Notify UI of connection");
|
||||
|
||||
send_to_clients();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
|
||||
AltosDebug.init(this);
|
||||
|
||||
// Initialise preferences
|
||||
AltosDroidPreferences.init(this);
|
||||
|
||||
// Get local Bluetooth adapter
|
||||
bluetooth_adapter = BluetoothAdapter.getDefaultAdapter();
|
||||
|
||||
telemetry_state = new TelemetryState();
|
||||
|
||||
// Create a reference to the NotificationManager so that we can update our notifcation text later
|
||||
//mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
|
||||
|
||||
telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
|
||||
telemetry_state.address = null;
|
||||
|
||||
/* Pull the saved state information out of the preferences database
|
||||
*/
|
||||
ArrayList<Integer> serials = AltosPreferences.list_states();
|
||||
|
||||
telemetry_state.latest_serial = AltosPreferences.latest_state();
|
||||
|
||||
telemetry_state.quiet = true;
|
||||
|
||||
AltosDebug.debug("latest serial %d\n", telemetry_state.latest_serial);
|
||||
|
||||
for (int serial : serials) {
|
||||
AltosState saved_state = AltosPreferences.state(serial);
|
||||
if (saved_state != null) {
|
||||
AltosDebug.debug("recovered old state serial %d flight %d",
|
||||
serial,
|
||||
saved_state.cal_data().flight);
|
||||
if (saved_state.gps != null)
|
||||
AltosDebug.debug("\tposition %f,%f",
|
||||
saved_state.gps.lat,
|
||||
saved_state.gps.lon);
|
||||
telemetry_state.put(serial, saved_state);
|
||||
} else {
|
||||
AltosDebug.debug("Failed to recover state for %d", serial);
|
||||
AltosPreferences.remove_state(serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String createNotificationChannel(String channelId, String channelName) {
|
||||
NotificationChannel chan = new NotificationChannel(
|
||||
channelId, channelName, NotificationManager.IMPORTANCE_NONE);
|
||||
chan.setLightColor(Color.BLUE);
|
||||
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
|
||||
NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
service.createNotificationChannel(chan);
|
||||
return channelId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
AltosDebug.debug("Received start id %d: %s", startId, intent);
|
||||
int flag;
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 31) // android.os.Build.VERSION_CODES.S
|
||||
flag = 33554432; // PendingIntent.FLAG_MUTABLE
|
||||
else
|
||||
flag = 0;
|
||||
|
||||
// The PendingIntent to launch our activity if the user selects this notification
|
||||
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
|
||||
new Intent(this, AltosDroid.class), flag);
|
||||
|
||||
String channelId =
|
||||
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
? createNotificationChannel("altosdroid_telemetry", "AltosDroid Telemetry Service")
|
||||
: "";
|
||||
|
||||
// Create notification to be displayed while the service runs
|
||||
Notification notification = new NotificationCompat.Builder(this, channelId)
|
||||
.setContentTitle(getText(R.string.telemetry_service_label))
|
||||
.setContentText(getText(R.string.telemetry_service_started))
|
||||
.setContentIntent(contentIntent)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.drawable.am_status_c)
|
||||
// .setLargeIcon(R.drawable.am_status_c)
|
||||
.build();
|
||||
|
||||
// Move us into the foreground.
|
||||
startForeground(NOTIFICATION, notification);
|
||||
|
||||
/* Start bluetooth if we don't have a connection already */
|
||||
if (intent != null &&
|
||||
(telemetry_state.connect == TelemetryState.CONNECT_NONE ||
|
||||
telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED))
|
||||
{
|
||||
String action = intent.getAction();
|
||||
|
||||
if (action.equals(AltosDroid.ACTION_BLUETOOTH)) {
|
||||
DeviceAddress address = AltosDroidPreferences.active_device();
|
||||
if (address != null && !address.address.startsWith("USB"))
|
||||
start_altos_bluetooth(address, false);
|
||||
}
|
||||
}
|
||||
|
||||
// We want this service to continue running until it is explicitly
|
||||
// stopped, so return sticky.
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
|
||||
// Stop the bluetooth Comms threads
|
||||
disconnect(true);
|
||||
|
||||
// Demote us from the foreground, and cancel the persistent notification.
|
||||
stopForeground(true);
|
||||
|
||||
// Tell the user we stopped.
|
||||
Toast.makeText(this, R.string.telemetry_service_stopped, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return messenger.getBinder();
|
||||
}
|
||||
|
||||
/* AltosIdleMonitorListener */
|
||||
public void update(AltosState state, AltosListenerState listener_state) {
|
||||
if (state != null)
|
||||
AltosDebug.debug("update call %s freq %7.3f", state.cal_data().callsign, state.frequency);
|
||||
telemetry_state.put(state.cal_data().serial, state);
|
||||
telemetry_state.receiver_battery = listener_state.battery;
|
||||
send_to_clients();
|
||||
}
|
||||
|
||||
public void failed() {
|
||||
}
|
||||
|
||||
public void error(String reason) {
|
||||
stop_idle_monitor();
|
||||
}
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.altoslib_14.*;
|
||||
|
||||
public class TelemetryState {
|
||||
public static final int CONNECT_NONE = 0;
|
||||
public static final int CONNECT_DISCONNECTED = 1;
|
||||
public static final int CONNECT_CONNECTING = 2;
|
||||
public static final int CONNECT_CONNECTED = 3;
|
||||
|
||||
int connect;
|
||||
DeviceAddress address;
|
||||
AltosConfigData config;
|
||||
int crc_errors;
|
||||
double receiver_battery;
|
||||
double frequency;
|
||||
int telemetry_rate;
|
||||
|
||||
boolean idle_mode;
|
||||
boolean quiet;
|
||||
|
||||
private HashMap<Integer,AltosState> states;
|
||||
|
||||
int latest_serial;
|
||||
long latest_received_time;
|
||||
|
||||
public void put(int serial, AltosState state) {
|
||||
long received_time = state.received_time;
|
||||
if (received_time > latest_received_time || latest_serial == 0) {
|
||||
latest_serial = serial;
|
||||
latest_received_time = received_time;
|
||||
}
|
||||
states.put(serial, state);
|
||||
}
|
||||
|
||||
public AltosState get(int serial) {
|
||||
if (states.containsKey(serial))
|
||||
return states.get(serial);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void remove(int serial) {
|
||||
states.remove((Integer) serial);
|
||||
}
|
||||
|
||||
public Set<Integer> keySet() {
|
||||
return states.keySet();
|
||||
}
|
||||
|
||||
public Collection<AltosState> values() {
|
||||
return states.values();
|
||||
}
|
||||
|
||||
public boolean containsKey(int serial) {
|
||||
return states.containsKey(serial);
|
||||
}
|
||||
|
||||
public TelemetryState() {
|
||||
connect = CONNECT_NONE;
|
||||
config = null;
|
||||
states = new HashMap<Integer,AltosState>();
|
||||
crc_errors = 0;
|
||||
receiver_battery = AltosLib.MISSING;
|
||||
frequency = AltosPreferences.frequency(0);
|
||||
telemetry_rate = AltosPreferences.telemetry_rate(0);
|
||||
}
|
||||
}
|
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright © 2020 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, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Intent;
|
||||
import android.content.Context;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.IBinder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
import android.os.Parcelable;
|
||||
import android.os.Parcel;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import android.app.AlertDialog;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.location.LocationListener;
|
||||
import android.hardware.usb.*;
|
||||
import android.content.pm.PackageManager;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class Tracker implements CharSequence, Comparable, Parcelable {
|
||||
int serial;
|
||||
String call;
|
||||
double frequency;
|
||||
long received_time;
|
||||
String display;
|
||||
|
||||
private void make_display() {
|
||||
if (frequency == 0.0)
|
||||
display = "Auto";
|
||||
else if (frequency == AltosLib.MISSING) {
|
||||
display = String.format("%-8.8s %6d", call, serial);
|
||||
} else {
|
||||
display = String.format("%-8.8s %7.3f %6d", call, frequency, serial);
|
||||
}
|
||||
}
|
||||
|
||||
public Tracker(int serial, String call, double frequency, long received_time) {
|
||||
if (call == null)
|
||||
call = "none";
|
||||
|
||||
this.serial = serial;
|
||||
this.call = call;
|
||||
this.frequency = frequency;
|
||||
this.received_time = received_time;
|
||||
make_display();
|
||||
}
|
||||
|
||||
public Tracker(int serial, String call, double frequency) {
|
||||
this(serial, call, frequency, 0);
|
||||
}
|
||||
|
||||
public Tracker(AltosState s) {
|
||||
this(s == null ? 0 : s.cal_data().serial,
|
||||
s == null ? null : s.cal_data().callsign,
|
||||
s == null ? 0.0 : s.frequency,
|
||||
s == null ? 0 : s.received_time);
|
||||
}
|
||||
|
||||
/* CharSequence */
|
||||
public char charAt(int index) {
|
||||
return display.charAt(index);
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return display.length();
|
||||
}
|
||||
|
||||
public CharSequence subSequence(int start, int end) throws IndexOutOfBoundsException {
|
||||
return display.subSequence(start, end);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return display.toString();
|
||||
}
|
||||
|
||||
/* Comparable */
|
||||
public int compareTo (Object other) {
|
||||
Tracker o = (Tracker) other;
|
||||
if (frequency == 0.0) {
|
||||
if (o.frequency == 0.0)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
if (o.frequency == 0.0)
|
||||
return 1;
|
||||
|
||||
int a = serial - o.serial;
|
||||
int b = call.compareTo(o.call);
|
||||
int c = (int) Math.signum(frequency - o.frequency);
|
||||
|
||||
if (b != 0)
|
||||
return b;
|
||||
if (c != 0)
|
||||
return c;
|
||||
return a;
|
||||
}
|
||||
|
||||
/* Parcelable */
|
||||
|
||||
public int describeContents() {
|
||||
AltosDebug.debug("describe contents %d", serial);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
AltosDebug.debug("write to parcel %s", display);
|
||||
out.writeInt(serial);
|
||||
out.writeString(call);
|
||||
out.writeDouble(frequency);
|
||||
out.writeLong(received_time);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<Tracker> CREATOR
|
||||
= new Parcelable.Creator<Tracker>() {
|
||||
public Tracker createFromParcel(Parcel in) {
|
||||
AltosDebug.debug("createFromParcel");
|
||||
return new Tracker(in);
|
||||
}
|
||||
|
||||
public Tracker[] newArray(int size) {
|
||||
AltosDebug.debug("newArray %d", size);
|
||||
return new Tracker[size];
|
||||
}
|
||||
};
|
||||
|
||||
/* newer (-1), same (0), older(1) */
|
||||
public int compareAge(Tracker o) {
|
||||
if (received_time == o.received_time)
|
||||
return 0;
|
||||
if (received_time == 0)
|
||||
return -1;
|
||||
if (o.received_time == 0)
|
||||
return 1;
|
||||
if (received_time > o.received_time)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int compareCall(Tracker o) {
|
||||
int v = call.compareTo(o.call);
|
||||
if (v == 0)
|
||||
return v;
|
||||
if (call.equals("auto"))
|
||||
return -1;
|
||||
if (o.call.equals("auto"))
|
||||
return 1;
|
||||
return v;
|
||||
}
|
||||
|
||||
public int compareSerial(Tracker o) {
|
||||
return serial - o.serial;
|
||||
}
|
||||
|
||||
public int compareFrequency(Tracker o) {
|
||||
return (int) Math.signum(frequency - o.frequency);
|
||||
}
|
||||
|
||||
private Tracker(Parcel in) {
|
||||
serial = in.readInt();
|
||||
call = in.readString();
|
||||
frequency = in.readDouble();
|
||||
received_time = in.readLong();
|
||||
make_display();
|
||||
AltosDebug.debug("Create from parcel %s", display);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,5 @@
|
||||
all:
|
||||
make -C ../../../../../../.. $@
|
||||
|
||||
%:
|
||||
make -C ../../../../../../.. $@
|
BIN
altosdroid/app/src/main/res/drawable-hdpi/am_status_c.png
Normal file
After Width: | Height: | Size: 994 B |
BIN
altosdroid/app/src/main/res/drawable-hdpi/am_status_g.png
Normal file
After Width: | Height: | Size: 804 B |
BIN
altosdroid/app/src/main/res/drawable-hdpi/app_icon.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 2.0 KiB |
BIN
altosdroid/app/src/main/res/drawable-mdpi/am_status_c.png
Normal file
After Width: | Height: | Size: 703 B |
BIN
altosdroid/app/src/main/res/drawable-mdpi/am_status_g.png
Normal file
After Width: | Height: | Size: 595 B |
After Width: | Height: | Size: 1.2 KiB |
BIN
altosdroid/app/src/main/res/drawable/app_icon.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
altosdroid/app/src/main/res/drawable/pad.png
Normal file
After Width: | Height: | Size: 584 B |
BIN
altosdroid/app/src/main/res/drawable/rocket.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
199
altosdroid/app/src/main/res/layout/altosdroid.xml
Normal file
@@ -0,0 +1,199 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Copyright © 2012-2013 Mike Beattie <mike@ethernal.org>
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:baselineAligned="true"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/callsign_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/callsign_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/callsign_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/callsign_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/callsign_label"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/serial_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/serial_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/serial_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/serial_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/serial_label"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/flight_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/flight_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/flight_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/flight_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/flight_label"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/state_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/state_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/state_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/state_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/state_label"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/rssi_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rssi_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/rssi_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rssi_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/rssi_label"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/age_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/age_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/age_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/age_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/age_label"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<TabHost
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/tabhost"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TabWidget
|
||||
android:id="@android:id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:orientation="horizontal" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@android:id/tabcontent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="0" />
|
||||
|
||||
<org.altusmetrum.AltosDroid.AltosViewPager
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
</LinearLayout>
|
||||
</TabHost>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/version"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="10dip"
|
||||
android:layout_weight="0"
|
||||
android:gravity="bottom|right"
|
||||
android:textSize="7sp"
|
||||
android:typeface="monospace" />
|
||||
|
||||
</LinearLayout>
|
39
altosdroid/app/src/main/res/layout/custom_title.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
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.
|
||||
-->
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
>
|
||||
<TextView android:id="@+id/title_left_text"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
style="?android:attr/windowTitleStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
<TextView android:id="@+id/title_right_text"
|
||||
android:layout_alignParentRight="true"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:textColor="#fff"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
</RelativeLayout>
|
63
altosdroid/app/src/main/res/layout/device_list.xml
Normal file
@@ -0,0 +1,63 @@
|
||||
<?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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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"
|
||||
>
|
||||
<Button android:id="@+id/button_scan"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/button_scan"
|
||||
/>
|
||||
<TextView android:id="@+id/title_new_devices"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/title_other_devices"
|
||||
android:visibility="gone"
|
||||
android:background="#666"
|
||||
android:textColor="#fff"
|
||||
android:paddingLeft="5dp"
|
||||
/>
|
||||
<ListView android:id="@+id/new_devices"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:fadeScrollbars="false"
|
||||
android:scrollbars="vertical"
|
||||
/>
|
||||
<TextView android:id="@+id/title_paired_devices"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/title_paired_devices"
|
||||
android:visibility="gone"
|
||||
android:background="#666"
|
||||
android:textColor="#fff"
|
||||
android:paddingLeft="5dp"
|
||||
/>
|
||||
<ListView android:id="@+id/paired_devices"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:fadeScrollbars="false"
|
||||
android:scrollbars="vertical"
|
||||
/>
|
||||
</LinearLayout>
|
20
altosdroid/app/src/main/res/layout/device_name.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
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.
|
||||
-->
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
/>
|
34
altosdroid/app/src/main/res/layout/frequency.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
<TextView
|
||||
android:id="@+id/frequency"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:padding="10dp"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
</LinearLayout>
|
67
altosdroid/app/src/main/res/layout/idle_mode.xml
Normal file
@@ -0,0 +1,67 @@
|
||||
<?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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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"
|
||||
>
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
<TextView android:id="@+id/set_callsign_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/set_callsign_label"
|
||||
/>
|
||||
<EditText android:id="@+id/set_callsign"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/set_callsign_label"/>
|
||||
</LinearLayout>
|
||||
<TextView android:id="@+id/frequency"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
/>
|
||||
<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>
|
39
altosdroid/app/src/main/res/layout/igniter_status.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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>
|
49
altosdroid/app/src/main/res/layout/igniters.xml
Normal file
@@ -0,0 +1,49 @@
|
||||
<?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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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>
|
97
altosdroid/app/src/main/res/layout/manage_frequencies.xml
Normal file
@@ -0,0 +1,97 @@
|
||||
<?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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/set_layout"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
<EditText
|
||||
android:id="@+id/set_frequency"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:padding="10dp"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/frequency"
|
||||
android:inputType="number|numberDecimal"/>
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/mhz"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:padding="10dp"
|
||||
android:layout_weight="0"
|
||||
android:text="@string/mhz"
|
||||
/>
|
||||
<EditText
|
||||
android:id="@+id/set_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:padding="10dp"
|
||||
android:layout_weight="2"
|
||||
android:hint="@string/description"
|
||||
/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
<Button android:id="@+id/set"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/set"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
|
||||
<Button android:id="@+id/remove"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/remove"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
|
||||
<Button android:id="@+id/done"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/done"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<ListView android:id="@+id/frequencies"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:fadeScrollbars="false"
|
||||
android:scrollbars="vertical"
|
||||
android:choiceMode="singleChoice"
|
||||
/>
|
||||
|
||||
|
||||
</LinearLayout>
|
132
altosdroid/app/src/main/res/layout/map_preload.xml
Normal file
@@ -0,0 +1,132 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright © 2015 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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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"
|
||||
>
|
||||
<ScrollView android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<TextView android:id="@+id/preload_site_label"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_site_label"
|
||||
/>
|
||||
<Spinner android:id="@+id/preload_site_list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:prompt="@string/preload_site_label"
|
||||
android:spinnerMode="dropdown"
|
||||
/>
|
||||
<TextView android:id="@+id/preload_latitude_label"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_latitude_label"
|
||||
/>
|
||||
<EditText android:id="@+id/preload_latitude"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/preload_latitude_label"
|
||||
android:inputType="number|numberSigned|numberDecimal"/>
|
||||
<TextView android:id="@+id/preload_longitude_label"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_longitude_label"
|
||||
/>
|
||||
<EditText android:id="@+id/preload_longitude"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/preload_longitude_label"
|
||||
android:inputType="number|numberSigned|numberDecimal"/>
|
||||
<TextView android:id="@+id/preload_types"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_types"
|
||||
/>
|
||||
<!--
|
||||
<CheckBox android:id="@+id/preload_hybrid"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_hybrid"
|
||||
/>
|
||||
<CheckBox android:id="@+id/preload_satellite"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_satellite"
|
||||
/>
|
||||
<CheckBox android:id="@+id/preload_roadmap"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_roadmap"
|
||||
/>
|
||||
<CheckBox android:id="@+id/preload_terrain"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_terrain"
|
||||
/>
|
||||
-->
|
||||
<TextView android:id="@+id/preload_min_zoom_label"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_min_zoom"
|
||||
/>
|
||||
<Spinner android:id="@+id/preload_min_zoom"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:prompt="@string/preload_min_zoom"
|
||||
android:spinnerMode="dropdown"
|
||||
/>
|
||||
<TextView android:id="@+id/preload_max_zoom_label"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_max_zoom"
|
||||
/>
|
||||
<Spinner android:id="@+id/preload_max_zoom"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:prompt="@string/preload_max_zoom"
|
||||
android:spinnerMode="dropdown"
|
||||
/>
|
||||
<TextView android:id="@+id/preload_radius_label"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_radius"
|
||||
/>
|
||||
<Spinner android:id="@+id/preload_radius"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:prompt="@string/preload_radius"
|
||||
android:spinnerMode="dropdown"
|
||||
/>
|
||||
<Button android:id="@+id/preload_load"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_load"
|
||||
/>
|
||||
<ProgressBar android:id="@+id/preload_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
48
altosdroid/app/src/main/res/layout/map_type.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright © 2015 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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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"
|
||||
>
|
||||
<Button android:id="@+id/map_type_hybrid"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_hybrid"
|
||||
android:onClick="selectType"
|
||||
/>
|
||||
<Button android:id="@+id/map_type_satellite"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_satellite"
|
||||
android:onClick="selectType"
|
||||
/>
|
||||
<Button android:id="@+id/map_type_roadmap"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_roadmap"
|
||||
android:onClick="selectType"
|
||||
/>
|
||||
<Button android:id="@+id/map_type_terrain"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_terrain"
|
||||
android:onClick="selectType"
|
||||
/>
|
||||
</LinearLayout>
|
144
altosdroid/app/src/main/res/layout/setup.xml
Normal file
@@ -0,0 +1,144 @@
|
||||
<?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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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"
|
||||
>
|
||||
<TableLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:stretchColumns="2,3"
|
||||
android:layout_weight="0"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/select_rate_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/telemetry_rate"
|
||||
/>
|
||||
<Spinner android:id="@+id/select_rate"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:prompt="@string/telemetry_rate"
|
||||
android:spinnerMode="dropdown"
|
||||
/>
|
||||
</TableRow>
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/set_units_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/set_units"
|
||||
/>
|
||||
<Spinner android:id="@+id/set_units"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:prompt="@string/set_units"
|
||||
android:spinnerMode="dropdown"
|
||||
/>
|
||||
</TableRow>
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/font_size_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/font_size"
|
||||
/>
|
||||
<Spinner android:id="@+id/font_size"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:prompt="@string/font_size"
|
||||
android:spinnerMode="dropdown"
|
||||
/>
|
||||
</TableRow>
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/map_type_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/map_type"
|
||||
/>
|
||||
<Spinner android:id="@+id/map_type"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:prompt="@string/map_type"
|
||||
android:spinnerMode="dropdown"
|
||||
/>
|
||||
</TableRow>
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/map_source_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/map_source"
|
||||
/>
|
||||
<Spinner android:id="@+id/map_source"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:prompt="@string/map_source"
|
||||
android:spinnerMode="dropdown"
|
||||
/>
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
<Button android:id="@+id/preload_maps"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preload_maps"
|
||||
/>
|
||||
<Button android:id="@+id/manage_frequencies"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/manage_frequencies"
|
||||
/>
|
||||
<Button android:id="@+id/done"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/done"
|
||||
/>
|
||||
</LinearLayout>
|
430
altosdroid/app/src/main/res/layout/tab_flight.xml
Normal file
@@ -0,0 +1,430 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TableLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:stretchColumns="2,3"
|
||||
android:layout_weight="0"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/speed_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/speed_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/speed_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/height_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/height_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/height_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/altitude_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/altitude_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/altitude_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/tilt_view"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tilt_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/tilt_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tilt_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_speed_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/max_speed_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_speed_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_height_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/max_height_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_height_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_altitude_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/max_altitude_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_altitude_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/elevation_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/elevation_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/elevation_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/range_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/range_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/range_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bearing_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/bearing_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bearing_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/compass_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_column="3"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/distance_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/gnd_distance_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/distance_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/lat_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/latitude_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/lat_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/lon_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/longitude_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/lon_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/apogee_view"
|
||||
android:visibility="gone"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/apogee_redled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:contentDescription="@string/apogee_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/apogee_greenled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:contentDescription="@string/apogee_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/apogee_voltage_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toRightOf="@id/apogee_greenled"
|
||||
android:text="@string/apogee_voltage_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/apogee_voltage_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/main_view"
|
||||
android:visibility="gone"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/main_redled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/main_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/main_greenled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:contentDescription="@string/main_voltage_label"
|
||||
android:paddingRight="5dp"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_voltage_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toRightOf="@id/main_greenled"
|
||||
android:text="@string/main_voltage_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_voltage_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
15
altosdroid/app/src/main/res/layout/tab_layout.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/customTabLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:id="@+id/tabLabel"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:textColor="#ffffff"
|
||||
android:gravity="center_horizontal"
|
||||
android:background="#808080"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_centerHorizontal="true" />
|
||||
</RelativeLayout>
|
151
altosdroid/app/src/main/res/layout/tab_map.xml
Normal file
@@ -0,0 +1,151 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="true"
|
||||
android:orientation="horizontal"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/map_online"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1">
|
||||
</LinearLayout>
|
||||
|
||||
<org.altusmetrum.AltosDroid.AltosMapOffline
|
||||
android:id="@+id/map_offline"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
</FrameLayout>
|
||||
|
||||
<TableLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:stretchColumns="1,3"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="true"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/distance_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="4dp"
|
||||
android:text="@string/distance_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/distance_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bearing_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="4dp"
|
||||
android:text="@string/bearing_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bearing_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
|
||||
<TableLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:stretchColumns="1,2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="true"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/target_pos_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="4dp"
|
||||
android:text="@string/target_pos_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/target_lat_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/target_lon_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_pos_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="4dp"
|
||||
android:text="@string/receiver_pos_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_lat_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_lon_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
565
altosdroid/app/src/main/res/layout/tab_pad.xml
Normal file
@@ -0,0 +1,565 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TableLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:stretchColumns="2,3"
|
||||
android:layout_weight="0"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/battery_redled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/battery_voltage_label"
|
||||
android:src="@drawable/grayled"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/battery_greenled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:contentDescription="@string/battery_voltage_label"
|
||||
android:paddingRight="5dp"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/battery_voltage_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/battery_voltage_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/battery_voltage_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
/>
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/receiver_row"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/receiver_redled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/receiver_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/receiver_greenled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/receiver_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_voltage_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/receiver_voltage_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_voltage_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logging_redled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/logging_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logging_greenled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/logging_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/logging_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/logging_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/logging_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/logging_label"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/gps_locked_redled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/gps_locked_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/gps_locked_greenled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/gps_locked_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gps_locked_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/gps_locked_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gps_locked_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/gps_locked_label"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/gps_ready_redled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/gps_ready_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/gps_ready_greenled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/gps_ready_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gps_ready_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/gps_ready_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gps_ready_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/gps_ready_label"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/apogee_row"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/apogee_redled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/apogee_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/apogee_greenled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/apogee_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/apogee_voltage_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/apogee_voltage_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/apogee_voltage_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/main_row"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/main_redled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/main_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/main_greenled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/main_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_voltage_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/main_voltage_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_voltage_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/ignite_a_row"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ignite_a_redled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/ignite_a_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ignite_a_greenled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/ignite_a_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ignite_a_voltage_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/ignite_a_voltage_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ignite_a_voltage_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/ignite_b_row"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ignite_b_redled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/ignite_b_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ignite_b_greenled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/ignite_b_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ignite_b_voltage_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/ignite_b_voltage_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ignite_b_voltage_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/ignite_c_row"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ignite_c_redled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/ignite_c_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ignite_c_greenled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/ignite_c_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ignite_c_voltage_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/ignite_c_voltage_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ignite_c_voltage_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/ignite_d_row"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ignite_d_redled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/ignite_d_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ignite_d_greenled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerInside"
|
||||
android:paddingRight="5dp"
|
||||
android:contentDescription="@string/ignite_d_voltage_label"
|
||||
android:src="@drawable/grayled" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ignite_d_voltage_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/ignite_d_voltage_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ignite_d_voltage_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/tilt_view"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tilt_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/tilt_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tilt_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_lat_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/receiver_latitude_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_lat_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_lon_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/receiver_longitude_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_lon_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_alt_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_column="2"
|
||||
android:text="@string/receiver_altitude_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_alt_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
244
altosdroid/app/src/main/res/layout/tab_recover.xml
Normal file
@@ -0,0 +1,244 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TableLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:stretchColumns="0,1"
|
||||
android:layout_weight="0"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bearing_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/bearing_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bearing_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/direction_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/direction_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/direction_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/distance_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/distance_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/distance_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/target_lat_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/target_latitude_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/target_lat_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/target_lon_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/target_longitude_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/target_lon_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_lat_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/receiver_latitude_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_lat_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_lon_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/receiver_longitude_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiver_lon_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""/>
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_height_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/max_height_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_height_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_speed_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/max_speed_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_speed_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_accel_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/max_accel_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_accel_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
52
altosdroid/app/src/main/res/layout/tracker_ent.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Copyright © 2020 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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<TableRow
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
<TextView
|
||||
android:id="@+id/call_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="3dp"
|
||||
android:text="" />
|
||||
<TextView
|
||||
android:id="@+id/serial_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="3dp"
|
||||
android:text="" />
|
||||
<TextView
|
||||
android:id="@+id/frequency_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="3dp"
|
||||
android:text="" />
|
||||
<TextView
|
||||
android:id="@+id/age_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</TableRow>
|
68
altosdroid/app/src/main/res/layout/tracker_list.xml
Normal file
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
|
||||
Copyright © 2020 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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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"
|
||||
>
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
>
|
||||
<TableLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/tracker_list"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:stretchColumns="1,2,3,4">
|
||||
<TableRow
|
||||
android:id="@+id/tracker_row"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:padding="2dip"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
<RadioButton
|
||||
android:id="@+id/call_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/callsign_label" />
|
||||
<RadioButton
|
||||
android:id="@+id/serial_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/serial_label" />
|
||||
<RadioButton
|
||||
android:id="@+id/frequency_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/freq_label" />
|
||||
<RadioButton
|
||||
android:id="@+id/age_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/age_label" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
47
altosdroid/app/src/main/res/menu/option_menu.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright © 2015 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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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.
|
||||
-->
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/connect_scan"
|
||||
android:icon="@android:drawable/ic_menu_search"
|
||||
android:title="@string/connect_device" />
|
||||
<item android:id="@+id/disconnect"
|
||||
android:icon="@android:drawable/ic_notification_clear_all"
|
||||
android:title="@string/disconnect_device" />
|
||||
<item android:id="@+id/select_freq"
|
||||
android:icon="@android:drawable/ic_menu_preferences"
|
||||
android:title="@string/select_freq" />
|
||||
|
||||
<item android:id="@+id/select_tracker"
|
||||
android:icon="@android:drawable/ic_menu_view"
|
||||
android:title="@string/select_tracker"/>
|
||||
<item android:id="@+id/delete_track"
|
||||
android:icon="@android:drawable/ic_notification_clear_all"
|
||||
android:title="@string/delete_track"/>
|
||||
|
||||
<item android:id="@+id/setup"
|
||||
android:icon="@android:drawable/ic_menu_preferences"
|
||||
android:title="@string/setup" />
|
||||
<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" />
|
||||
</menu>
|
21
altosdroid/app/src/main/res/values/Colors.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright © 2015 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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
<color name="old_color">#ffff4040</color>
|
||||
</resources>
|
44
altosdroid/app/src/main/res/values/CustomTheme.xml
Normal file
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<style name="Small" parent="android:Theme.Material.Light">
|
||||
<item name="android:textColor">#000000</item>
|
||||
<item name="android:textSize">12sp</item>
|
||||
<item name="android:lineHeight">14sp</item>
|
||||
</style>
|
||||
<style name="Medium" parent="android:Theme.Material.Light">
|
||||
<item name="android:textColor">#000000</item>
|
||||
<item name="android:textSize">15sp</item>
|
||||
<item name="android:lineHeight">18sp</item>
|
||||
</style>
|
||||
<style name="Large" parent="android:Theme.Material.Light">
|
||||
<item name="android:textColor">#000000</item>
|
||||
<item name="android:textSize">20sp</item>
|
||||
<item name="android:lineHeight">40sp</item>
|
||||
</style>
|
||||
<style name="Extra" parent="android:Theme.Material.Light">
|
||||
<item name="android:textColor">#000000</item>
|
||||
<item name="android:textSize">27sp</item>
|
||||
<item name="android:lineHeight">32sp</item>
|
||||
</style>
|
||||
<style name="Small.Dialog" parent="android:Theme.Material.Light.Dialog">
|
||||
<item name="android:textColor">#000000</item>
|
||||
<item name="android:textSize">12sp</item>
|
||||
<item name="android:lineHeight">14sp</item>
|
||||
</style>
|
||||
<style name="Medium.Dialog" parent="android:Theme.Material.Light.Dialog">
|
||||
<item name="android:textColor">#000000</item>
|
||||
<item name="android:textSize">15sp</item>
|
||||
<item name="android:lineHeight">18sp</item>
|
||||
</style>
|
||||
<style name="Large.Dialog" parent="android:Theme.Material.Light.Dialog">
|
||||
<item name="android:textColor">#000000</item>
|
||||
<item name="android:textSize">20sp</item>
|
||||
<item name="android:lineHeight">40sp</item>
|
||||
</style>
|
||||
<style name="Extra.Dialog" parent="android:Theme.Material.Light.Dialog">
|
||||
<item name="android:textColor">#000000</item>
|
||||
<item name="android:textSize">27sp</item>
|
||||
<item name="android:lineHeight">32sp</item>
|
||||
</style>
|
||||
</resources>
|
156
altosdroid/app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright © 2012-2013 Mike Beattie <mike@ethernal.org>
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
<string name="app_name">AltosDroid</string>
|
||||
|
||||
<!-- AltosDroid -->
|
||||
<string name="bt_not_enabled">Bluetooth was not enabled.</string>
|
||||
<string name="title_connecting">connecting…</string>
|
||||
<string name="title_connected_to">connected</string>
|
||||
<string name="title_not_connected">not connected</string>
|
||||
|
||||
<!-- Options Menu -->
|
||||
<string name="connect_device">Connect a device</string>
|
||||
<string name="disconnect_device">Disconnect device</string>
|
||||
<string name="quit">Quit</string>
|
||||
<string name="setup">Setup</string>
|
||||
<string name="select_freq">Select radio frequency</string>
|
||||
<string name="select_rate">Select data rate</string>
|
||||
<string name="change_units">Change units</string>
|
||||
<string name="preload_maps">Load Maps</string>
|
||||
<string name="select_tracker">Select Tracker</string>
|
||||
<string name="delete_track">Delete Track</string>
|
||||
<string name="map_type">Map Type</string>
|
||||
<string name="map_source">Map Source</string>
|
||||
|
||||
<!-- MapTypeActivity -->
|
||||
<!-- <string name="map_type">Map Type</string> -->
|
||||
|
||||
<!-- DeviceListActivity -->
|
||||
<string name="scanning">Scanning…</string>
|
||||
<string name="select_device">Select device</string>
|
||||
<string name="none_paired">No devices have been paired</string>
|
||||
<string name="none_found">No devices found</string>
|
||||
<string name="title_paired_devices">Paired Devices</string>
|
||||
<string name="title_other_devices">Other Available Devices</string>
|
||||
<string name="button_scan">Scan for devices</string>
|
||||
|
||||
<!-- TrackerListActivity -->
|
||||
<string name="freq_label">Freq</string>
|
||||
|
||||
<!-- Service -->
|
||||
<string name="telemetry_service_label">AltosDroid Telemetry Service</string>
|
||||
<string name="telemetry_service_started">Telemetry Service Started</string>
|
||||
<string name="telemetry_service_stopped">Telemetry Service Stopped</string>
|
||||
|
||||
<!-- UI fields -->
|
||||
<!-- Header -->
|
||||
<string name="callsign_label">Call</string>
|
||||
<string name="serial_label">Serial</string>
|
||||
<string name="flight_label">Flight</string>
|
||||
<string name="state_label">State</string>
|
||||
<string name="rssi_label">RSSI</string>
|
||||
<string name="age_label">Age</string>
|
||||
|
||||
<!-- Tab fields -->
|
||||
<string name="height_label">Height</string>
|
||||
<string name="altitude_label">Altitude</string>
|
||||
<string name="speed_label">Speed</string>
|
||||
<string name="accel_label">Acceleration</string>
|
||||
<string name="tilt_label">Tilt</string>
|
||||
<string name="bearing_label">Bearing</string>
|
||||
<string name="direction_label">Direction</string>
|
||||
<string name="elevation_label">Elevation</string>
|
||||
<string name="range_label">Range</string>
|
||||
<string name="distance_label">Distance</string>
|
||||
<string name="gnd_distance_label">Ground Distance</string>
|
||||
<string name="max_height_label">Max Height</string>
|
||||
<string name="max_altitude_label">Max Altitude</string>
|
||||
<string name="max_speed_label">Max Speed</string>
|
||||
<string name="max_accel_label">Max Accel</string>
|
||||
<string name="battery_voltage_label">Battery</string>
|
||||
<string name="receiver_voltage_label">Receiver Battery</string>
|
||||
<string name="apogee_voltage_label">Apogee Igniter</string>
|
||||
<string name="main_voltage_label">Main Igniter</string>
|
||||
<string name="ignite_a_voltage_label">Igniter A</string>
|
||||
<string name="ignite_b_voltage_label">Igniter B</string>
|
||||
<string name="ignite_c_voltage_label">Igniter C</string>
|
||||
<string name="ignite_d_voltage_label">Igniter D</string>
|
||||
<string name="logging_label">Data Logging</string>
|
||||
<string name="gps_locked_label">GPS Locked</string>
|
||||
<string name="gps_ready_label">GPS Ready</string>
|
||||
<string name="latitude_label">Latitude</string>
|
||||
<string name="longitude_label">Longitude</string>
|
||||
<string name="target_pos_label">Tar</string>
|
||||
<string name="receiver_pos_label">Me</string>
|
||||
<string name="target_latitude_label">Tar Lat</string>
|
||||
<string name="target_longitude_label">Tar Lon</string>
|
||||
<string name="receiver_latitude_label">My Lat</string>
|
||||
<string name="receiver_longitude_label">My Lon</string>
|
||||
<string name="receiver_altitude_label">My Alt</string>
|
||||
|
||||
<!-- Map preload -->
|
||||
<string name="preload_site_label">Known Launch Sites</string>
|
||||
<string name="preload_latitude_label">Latitude</string>
|
||||
<string name="preload_longitude_label">Longitude</string>
|
||||
|
||||
<string name="preload_types">Map Types</string>
|
||||
<string name="preload_hybrid">Hybrid</string>
|
||||
<string name="preload_satellite">Satellite</string>
|
||||
<string name="preload_roadmap">Roadmap</string>
|
||||
<string name="preload_terrain">Terrain</string>
|
||||
<string name="preload_min_zoom">Minimum Zoom</string>
|
||||
<string name="preload_max_zoom">Maximum Zoom</string>
|
||||
<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">Monitor</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>
|
||||
|
||||
<!-- setup -->
|
||||
<string name="telemetry_rate">Telemetry Rate</string>
|
||||
<string name="set_units">Units</string>
|
||||
<!-- <string name="map_type">Map Type</string> -->
|
||||
<!-- <string name="map_source">Map Source</string> -->
|
||||
<!-- <string name="preload_maps">Preload Maps</string> -->
|
||||
<string name="font_size">Text Size</string>
|
||||
<string name="manage_frequencies">Manage Frequencies</string>
|
||||
<string name="done">OK</string>
|
||||
|
||||
<!-- manage frequencies -->
|
||||
<string name="set">Set</string>
|
||||
<string name="mhz">MHz</string>
|
||||
<string name="remove">Remove</string>
|
||||
<!-- <string name="done">OK</string> -->
|
||||
<string name="frequency">Frequency</string>
|
||||
<string name="description">Description</string>
|
||||
</resources>
|
6
altosdroid/app/src/main/res/xml/device_filter.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<usb-device vendor-id="65534" />
|
||||
<usb-device vendor-id="1027" />
|
||||
</resources>
|