From 18fe64cf2648568dd0bde5acd7b627f1ddb6917e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 22 Jun 2015 18:26:34 -0700 Subject: altosdroid: Display online/offline maps in same tab Make the map portion switchable between online and offline maps, leaving the rest of the tab alone. Signed-off-by: Keith Packard --- .../altusmetrum/AltosDroid/AltosMapOffline.java | 469 +++++++++++++++++++++ 1 file changed, 469 insertions(+) create mode 100644 altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java (limited to 'altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java') diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java new file mode 100644 index 00000000..3ff6ff25 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java @@ -0,0 +1,469 @@ +/* + * Copyright © 2015 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +import java.util.*; +import java.io.*; + +import org.altusmetrum.altoslib_7.*; + +import android.app.Activity; +import android.graphics.*; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.view.*; +import android.widget.*; +import android.location.Location; +import android.content.*; +import android.util.*; + +class Rocket implements Comparable { + AltosLatLon position; + String name; + long last_packet; + 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; + } + + Rocket(String name, AltosMapOffline map_offline) { + this.name = name; + this.map_offline = map_offline; + } + + public int compareTo(Object o) { + Rocket other = (Rocket) o; + + long diff = last_packet - other.last_packet; + + if (diff > 0) + return 1; + if (diff < 0) + return -1; + return 0; + } +} + +public class AltosMapOffline extends View implements ScaleGestureDetector.OnScaleGestureListener, AltosMapInterface, AltosDroidMapInterface { + ScaleGestureDetector scale_detector; + boolean scaling; + AltosMap map; + + AltosLatLon here; + 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 = cache.get(this, store, px_size, px_size); + + 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.loading: + message = "Loading..."; + break; + case AltosMapTile.bad_request: + message = "Internal error"; + break; + case AltosMapTile.failed: + message = "Network error, check connection"; + break; + case AltosMapTile.forbidden: + message = "Too many requests, try later"; + 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(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + super(listener, upper_left, center, zoom, maptype, px_size, 2); + } + + } + + public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + return new MapTile(listener, upper_left, center, zoom, maptype, px_size); + } + + 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); + } + } + + public AltosMapMark new_mark(double lat, double lon, int state) { + return new MapMark(lat, lon, state); + } + + 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) { + } + + 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 rockets = new HashMap(); + + 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 void draw_positions() { + line.set_a(map.last_position); + line.set_b(here); + line.paint(); + draw_bitmap(pad, pad_bitmap, pad_off_x, pad_off_y); + + Rocket[] rocket_array = rockets.values().toArray(new Rocket[0]); + + Arrays.sort(rocket_array); + for (Rocket rocket : rocket_array) + rocket.paint(); + draw_bitmap(here, here_bitmap, here_off_x, here_off_y); + } + + @Override public void invalidate() { + Rect r = new Rect(); + getDrawingRect(r); + super.invalidate(); + } + + @Override public void invalidate(int l, int t, int r, int b) { + Rect rect = new Rect(); + getDrawingRect(rect); + super.invalidate(); + } + + @Override + protected void onDraw(Canvas view_canvas) { + debug("onDraw"); + if (map == null) { + debug("MapView draw without map\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); + } + return true; + } + + double mapAccuracy; + + public void center(double lat, double lon, double accuracy) { + if (mapAccuracy < 0 || accuracy < mapAccuracy/10) { + 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) { + 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.states.keySet().toArray(new Integer[0]); + + /* remove deleted keys */ + for (int serial : old_serial) { + if (!telem_state.states.containsKey(serial)) + rockets.remove(serial); + } + + /* set remaining keys */ + + for (int serial : new_serial) { + Rocket rocket; + AltosState t_state = telem_state.states.get(serial); + if (rockets.containsKey(serial)) + rocket = rockets.get(serial); + else { + rocket = new Rocket(String.format("%d", serial), this); + rockets.put(serial, rocket); + } + if (t_state.gps != null) + rocket.set_position(new AltosLatLon(t_state.gps.lat, t_state.gps.lon), t_state.received_time); + } + } + if (receiver != null) { + here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude()); + } + } + + public void onCreateView(int map_type) { + map = new AltosMap(this); + map.set_maptype(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 set_map_type(int map_type) { + if (map != null) + map.set_maptype(map_type); + } + + public AltosMapOffline(Context context, AttributeSet attrs) { + super(context, attrs); + scale_detector = new ScaleGestureDetector(context, this); + } +} -- cgit v1.2.3 From 0f56903774d9e8bb033dfc0af6945e8ddc1d3065 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 22 Jun 2015 20:08:05 -0700 Subject: altosdroid: Select tracker by clicking on map This lets you pick a tracker from the map, rather than having to use the menu. Signed-off-by: Keith Packard --- altosdroid/Notebook | 24 ++++++--- .../src/org/altusmetrum/AltosDroid/AltosDroid.java | 48 ++++++++++++----- .../AltosDroid/AltosDroidMapInterface.java | 2 +- .../altusmetrum/AltosDroid/AltosMapOffline.java | 61 ++++++++++++++++++---- .../org/altusmetrum/AltosDroid/AltosMapOnline.java | 6 ++- .../src/org/altusmetrum/AltosDroid/TabMap.java | 12 +---- altoslib/AltosMap.java | 21 ++++++++ altoslib/AltosMapInterface.java | 2 + altoslib/AltosMapTransform.java | 7 +++ altosuilib/AltosUIMapNew.java | 5 ++ 10 files changed, 146 insertions(+), 42 deletions(-) (limited to 'altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java') diff --git a/altosdroid/Notebook b/altosdroid/Notebook index c0ba2098..6804aa5d 100644 --- a/altosdroid/Notebook +++ b/altosdroid/Notebook @@ -15,14 +15,6 @@ Desired AltosDroid feature list *) Monitor-idle mode - *) Select tracker by clicking map - - *) Auto select tracker after long delay - - *) Evaluate performance issues - - *) Merge offline/online maps into single tab with mode - *) Make voice responses depend on selected tab *) Monitor TeleMega igniters @@ -96,3 +88,19 @@ Completed features *) TeleBT battery voltage Done + + *) Evaluate performance issues + + Done. Offline maps were duplicating tabs at every redisplay. + + *) Merge offline/online maps into single tab with mode + + Done. + + *) Auto select tracker after long delay + + Done. + + *) Select tracker by clicking map + + Done. diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java index f6645105..c5da6d0e 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java @@ -18,10 +18,9 @@ package org.altusmetrum.AltosDroid; import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Timer; -import java.util.TimerTask; import java.text.*; +import java.util.*; +import java.io.*; import android.app.Activity; import android.app.PendingIntent; @@ -248,11 +247,13 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } } + int selected_serial = 0; int current_serial; long switch_time; void set_switch_time() { switch_time = System.currentTimeMillis(); + selected_serial = 0; } boolean registered_units_listener; @@ -262,6 +263,9 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { if (new_telemetry_state != null) telemetry_state = new_telemetry_state; + if (selected_serial != 0) + current_serial = selected_serial; + if (current_serial == 0) current_serial = telemetry_state.latest_serial; @@ -271,6 +275,7 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } serials = telemetry_state.states.keySet().toArray(new Integer[0]); + Arrays.sort(serials); update_title(telemetry_state); @@ -282,7 +287,9 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { int age = state_age(state); if (age < 20) aged = false; - if (switch_time != 0 && (switch_time - state.received_time) > 0) + if (current_serial == selected_serial) + aged = false; + else if (switch_time != 0 && (switch_time - state.received_time) > 0) aged = true; } @@ -828,13 +835,26 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { void select_tracker(int serial) { int i; - for (i = 0; i < serials.length; i++) - if (serials[i] == serial) - break; - if (i == serials.length) + + AltosDebug.debug("select tracker %d\n", serial); + + if (serial == selected_serial) { + AltosDebug.debug("%d already selected\n", serial); return; + } + + if (serial != 0) { + for (i = 0; i < serials.length; i++) + if (serials[i] == serial) + break; - current_serial = serial; + if (i == serials.length) { + AltosDebug.debug("attempt to select unknown tracker %d\n", serial); + return; + } + } + + current_serial = selected_serial = serial; update_state(null); } @@ -933,15 +953,19 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { return true; case R.id.select_tracker: if (serials != null) { - String[] trackers = new String[serials.length]; + String[] trackers = new String[serials.length+1]; + trackers[0] = "Auto"; for (int i = 0; i < serials.length; i++) - trackers[i] = String.format("%d", serials[i]); + trackers[i+1] = String.format("%d", serials[i]); AlertDialog.Builder builder_serial = new AlertDialog.Builder(this); builder_serial.setTitle("Select a tracker"); builder_serial.setItems(trackers, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { - select_tracker(serials[item]); + if (item == 0) + select_tracker(0); + else + select_tracker(serials[item-1]); } }); AlertDialog alert_serial = builder_serial.create(); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java index 681cd311..7aff1341 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java @@ -23,7 +23,7 @@ import android.location.Location; import org.altusmetrum.altoslib_7.*; public interface AltosDroidMapInterface { - public void onCreateView(int map_type); + public void onCreateView(AltosDroid altos_droid); public void set_visible(boolean visible); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java index 3ff6ff25..12dd2f25 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java @@ -36,7 +36,9 @@ import android.util.*; class Rocket implements Comparable { AltosLatLon position; String name; + int serial; long last_packet; + boolean active; AltosMapOffline map_offline; void paint() { @@ -49,14 +51,18 @@ class Rocket implements Comparable { this.last_packet = last_packet; } - Rocket(String name, AltosMapOffline map_offline) { - this.name = name; - this.map_offline = map_offline; + 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) @@ -65,12 +71,19 @@ class Rocket implements Comparable { 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 { ScaleGestureDetector scale_detector; boolean scaling; AltosMap map; + AltosDroid altos_droid; AltosLatLon here; AltosLatLon pad; @@ -236,6 +249,24 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal public void set_zoom_label(String label) { } + public void select_object(AltosLatLon latlon) { + if (map.transform == null) + return; + 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); + altos_droid.select_tracker(rocket.serial); + break; + } + } + } + class Line { AltosLatLon a, b; @@ -295,16 +326,20 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal } } + 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(map.last_position); line.set_b(here); line.paint(); draw_bitmap(pad, pad_bitmap, pad_off_x, pad_off_y); - Rocket[] rocket_array = rockets.values().toArray(new Rocket[0]); - - Arrays.sort(rocket_array); - for (Rocket rocket : rocket_array) + for (Rocket rocket : sorted_rockets()) rocket.paint(); draw_bitmap(here, here_bitmap, here_off_x, here_off_y); } @@ -379,6 +414,8 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal 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; } @@ -425,11 +462,13 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal if (rockets.containsKey(serial)) rocket = rockets.get(serial); else { - rocket = new Rocket(String.format("%d", serial), this); + rocket = new Rocket(serial, this); rockets.put(serial, rocket); } if (t_state.gps != null) rocket.set_position(new AltosLatLon(t_state.gps.lat, t_state.gps.lon), t_state.received_time); + if (state != null) + rocket.set_active(state.serial == serial); } } if (receiver != null) { @@ -437,9 +476,10 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal } } - public void onCreateView(int map_type) { + public void onCreateView(AltosDroid altos_droid) { + this.altos_droid = altos_droid; map = new AltosMap(this); - map.set_maptype(map_type); + map.set_maptype(altos_droid.map_type); pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad); /* arrow at the bottom of the launchpad image */ @@ -464,6 +504,7 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal public AltosMapOffline(Context context, AttributeSet attrs) { super(context, attrs); + this.altos_droid = altos_droid; scale_detector = new ScaleGestureDetector(context, this); } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java index 9503a0bd..3f5f32be 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java @@ -121,7 +121,11 @@ public class AltosMapOnline implements AltosDroidMapInterface { private AltosLatLon my_position = null; private AltosLatLon target_position = null; - public void onCreateView(final int map_type) { + private AltosDroid altos_droid; + + public void onCreateView(AltosDroid altos_droid) { + this.altos_droid = altos_droid; + final int map_type = altos_droid.map_type; mMapFragment = new SupportMapFragment() { @Override public void onActivityCreated(Bundle savedInstanceState) { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java index 9c39e105..cd59dfe7 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java @@ -57,14 +57,6 @@ public class TabMap extends AltosDroidTab { super.onCreate(savedInstanceState); } - private void make_offline_map() { - } - - private void make_online_map() { - map_online = new AltosMapOnline(view.getContext()); - map_online.onCreateView(altos_droid.map_type); - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = inflater.inflate(R.layout.tab_map, container, false); @@ -77,9 +69,9 @@ public class TabMap extends AltosDroidTab { 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_type); + map_offline.onCreateView(altos_droid); map_online = new AltosMapOnline(view.getContext()); - map_online.onCreateView(altos_droid.map_type); + map_online.onCreateView(altos_droid); set_map_source(AltosDroidPreferences.map_source()); return view; } diff --git a/altoslib/AltosMap.java b/altoslib/AltosMap.java index adf52ab9..8d12a180 100644 --- a/altoslib/AltosMap.java +++ b/altoslib/AltosMap.java @@ -391,6 +391,10 @@ public class AltosMap implements AltosMapTileListener, AltosMapStoreListener { AltosPointInt drag_start; + boolean dragged; + + static final double drag_far = 20; + private void drag(int x, int y) { if (drag_start == null) return; @@ -398,6 +402,11 @@ public class AltosMap implements AltosMapTileListener, AltosMapStoreListener { int dx = x - drag_start.x; int dy = y - drag_start.y; + double distance = Math.hypot(dx, dy); + + if (distance > drag_far) + dragged = true; + if (transform == null) { debug("Transform not set in drag\n"); return; @@ -410,6 +419,12 @@ public class AltosMap implements AltosMapTileListener, AltosMapStoreListener { private void drag_start(int x, int y) { drag_start = new AltosPointInt(x, y); + dragged = false; + } + + private void drag_stop(int x, int y) { + if (!dragged) + map_interface.select_object (transform.screen_lat_lon(new AltosPointInt(x,y))); } private void line_start(int x, int y) { @@ -442,6 +457,12 @@ public class AltosMap implements AltosMapTileListener, AltosMapStoreListener { line(x, y); } + public void touch_stop(int x, int y, boolean is_drag) { + notice_user_input(); + if (is_drag) + drag_stop(x, y); + } + public AltosMap(AltosMapInterface map_interface) { this.map_interface = map_interface; cache = new AltosMapCache(map_interface); diff --git a/altoslib/AltosMapInterface.java b/altoslib/AltosMapInterface.java index e6cb5971..7e8dd236 100644 --- a/altoslib/AltosMapInterface.java +++ b/altoslib/AltosMapInterface.java @@ -42,4 +42,6 @@ public interface AltosMapInterface { public abstract void set_zoom_label(String label); public abstract void debug(String format, Object ... arguments); + + public abstract void select_object(AltosLatLon latlon); } diff --git a/altoslib/AltosMapTransform.java b/altoslib/AltosMapTransform.java index 30994ecb..11ed4eb9 100644 --- a/altoslib/AltosMapTransform.java +++ b/altoslib/AltosMapTransform.java @@ -51,6 +51,13 @@ public class AltosMapTransform { return new AltosPointDouble(screen.x + offset_x, screen.y + offset_y); } + public double hypot(AltosLatLon a, AltosLatLon b) { + AltosPointDouble a_pt = point(a); + AltosPointDouble b_pt = point(b); + + return Math.hypot(a_pt.x - b_pt.x, a_pt.y - b_pt.y); + } + public AltosLatLon screen_lat_lon(AltosPointInt screen) { return lat_lon(screen_point(screen)); } diff --git a/altosuilib/AltosUIMapNew.java b/altosuilib/AltosUIMapNew.java index 246222bc..8ac18296 100644 --- a/altosuilib/AltosUIMapNew.java +++ b/altosuilib/AltosUIMapNew.java @@ -358,10 +358,15 @@ public class AltosUIMapNew extends JComponent implements AltosFlightDisplay, Alt zoom_label.setText(label); } + public void select_object(AltosLatLon latlon) { + debug("select at %f,%f\n", latlon.lat, latlon.lon); + } + public void debug(String format, Object ... arguments) { System.out.printf(format, arguments); } + /* AltosFlightDisplay interface */ public void set_font() { -- cgit v1.2.3 From 1513693602c2a4cab0783833d1844c066edabb71 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 22 Jun 2015 23:21:05 -0700 Subject: altosdroid: Fix line drawing to old tracker location Selecting an old tracker would often fail to switch the bearing line as it was using the map data instead of just using the local data for the relavant tracker. Signed-off-by: Keith Packard --- .../src/org/altusmetrum/AltosDroid/AltosMapOffline.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java') diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java index 12dd2f25..339b7894 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java @@ -86,6 +86,7 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal AltosDroid altos_droid; AltosLatLon here; + AltosLatLon there; AltosLatLon pad; Canvas canvas; @@ -334,7 +335,7 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal } private void draw_positions() { - line.set_a(map.last_position); + line.set_a(there); line.set_b(here); line.paint(); draw_bitmap(pad, pad_bitmap, pad_off_x, pad_off_y); @@ -465,8 +466,12 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal rocket = new Rocket(serial, this); rockets.put(serial, rocket); } - if (t_state.gps != null) - rocket.set_position(new AltosLatLon(t_state.gps.lat, t_state.gps.lon), t_state.received_time); + 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.serial == serial) + there = latlon; + } if (state != null) rocket.set_active(state.serial == serial); } -- cgit v1.2.3 From 06908e377b7b932bfe3f6dfc840a0a13340f32ce Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 25 Jun 2015 12:22:04 -0700 Subject: altosdroid: Class of offline map view widget changed Switch around AltosViewPager to match. Signed-off-by: Keith Packard --- .../altusmetrum/AltosDroid/AltosMapOffline.java | 2 +- .../org/altusmetrum/AltosDroid/AltosMapOnline.java | 2 +- .../org/altusmetrum/AltosDroid/AltosViewPager.java | 24 ++++++++++++---------- .../altusmetrum/AltosDroid/TelemetryService.java | 6 ++++++ 4 files changed, 21 insertions(+), 13 deletions(-) (limited to 'altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java') diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java index 339b7894..5240d61c 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java @@ -424,7 +424,7 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal double mapAccuracy; public void center(double lat, double lon, double accuracy) { - if (mapAccuracy < 0 || accuracy < mapAccuracy/10) { + if (mapAccuracy <= 0 || accuracy < mapAccuracy/10 || (map != null && !map.has_centre())) { if (map != null) map.maybe_centre(lat, lon); mapAccuracy = accuracy; diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java index b81cd394..7691a13e 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java @@ -275,7 +275,7 @@ public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarke center (my_position.lat, my_position.lon, accuracy); } - if (my_position != null && target_position != null) { + 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); } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java index e8299b09..f22298d7 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java @@ -34,17 +34,19 @@ public class AltosViewPager extends ViewPager { @Override protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { - if (v.getClass().getName().endsWith("MapView")) - 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); + + 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); } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java index 2d0ae65d..58bf8804 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java @@ -441,6 +441,12 @@ public class TelemetryService extends Service implements LocationListener { @Override public void onCreate() { + + AltosDebug.init(this); + + // Initialise preferences + AltosDroidPreferences.init(this); + // Get local Bluetooth adapter bluetooth_adapter = BluetoothAdapter.getDefaultAdapter(); -- cgit v1.2.3 From 51bf46248ca7ee3c817e62274b7366258c9f87cf Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 11 Jul 2015 19:53:06 -0700 Subject: altosdroid: Pop up menu of nearby trackers on map click This lets the user select one of potentially many overlapping trackers, and also makes it clear when the current tracker is being changed. Signed-off-by: Keith Packard --- .../src/org/altusmetrum/AltosDroid/AltosDroid.java | 16 ++++++ .../altusmetrum/AltosDroid/AltosMapOffline.java | 8 +-- .../org/altusmetrum/AltosDroid/AltosMapOnline.java | 60 ++++++++++++++++------ 3 files changed, 64 insertions(+), 20 deletions(-) (limited to 'altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java') diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java index 3a5026ee..4ac24053 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java @@ -855,6 +855,22 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { update_state(null); } + void touch_trackers(Integer[] serials) { + AlertDialog.Builder builder_tracker = new AlertDialog.Builder(this); + builder_tracker.setTitle("Select Tracker"); + final String[] trackers = new String[serials.length]; + for (int i = 0; i < serials.length; i++) + trackers[i] = String.format("%d", serials[i]); + builder_tracker.setItems(trackers, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + select_tracker(Integer.parseInt(trackers[item])); + } + }); + AlertDialog alert_tracker = builder_tracker.create(); + alert_tracker.show(); + } + void delete_track(int serial) { try { mService.send(Message.obtain(null, TelemetryService.MSG_DELETE_SERIAL, (Integer) serial)); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java index 5240d61c..0bf6ab20 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java @@ -253,6 +253,8 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal public void select_object(AltosLatLon latlon) { if (map.transform == null) return; + ArrayList near = new ArrayList(); + for (Rocket rocket : sorted_rockets()) { if (rocket.position == null) { debug("rocket %d has no position\n", rocket.serial); @@ -262,10 +264,11 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal 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); - altos_droid.select_tracker(rocket.serial); - break; + near.add(rocket.serial); } } + if (near.size() != 0) + altos_droid.touch_trackers(near.toArray(new Integer[0])); } class Line { @@ -359,7 +362,6 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal @Override protected void onDraw(Canvas view_canvas) { - debug("onDraw"); if (map == null) { debug("MapView draw without map\n"); return; diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java index 7691a13e..3b56fb54 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java @@ -21,15 +21,8 @@ import java.util.*; import org.altusmetrum.altoslib_7.*; -import com.google.android.gms.maps.CameraUpdateFactory; -import com.google.android.gms.maps.GoogleMap; -import com.google.android.gms.maps.SupportMapFragment; -import com.google.android.gms.maps.model.BitmapDescriptorFactory; -import com.google.android.gms.maps.model.LatLng; -import com.google.android.gms.maps.model.Marker; -import com.google.android.gms.maps.model.MarkerOptions; -import com.google.android.gms.maps.model.Polyline; -import com.google.android.gms.maps.model.PolylineOptions; +import com.google.android.gms.maps.*; +import com.google.android.gms.maps.model.*; import android.app.Activity; import android.graphics.Color; @@ -48,6 +41,7 @@ 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)); @@ -75,6 +69,8 @@ class RocketOnline implements Comparable { 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; } @@ -106,7 +102,7 @@ class RocketOnline implements Comparable { } } -public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarkerClickListener { +public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarkerClickListener, GoogleMap.OnMapClickListener { public SupportMapFragment mMapFragment; private GoogleMap mMap; private boolean mapLoaded = false; @@ -152,14 +148,43 @@ public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarke // getChildFragmentManager().beginTransaction().add(R.id.map, mMapFragment).commit(); // } - public boolean onMarkerClick(Marker marker) { - for (RocketOnline rocket : rockets.values()) { - if (rocket.marker.equals(marker)) { - altos_droid.select_tracker(rocket.serial); - return true; - } + 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() { + RocketOnline[] rocket_array = rockets.values().toArray(new RocketOnline[0]); + + Arrays.sort(rocket_array); + return rocket_array; + } + + public void onMapClick(LatLng lat_lng) { + ArrayList near = new ArrayList(); + + 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); } - return false; + + if (near.size() != 0) + altos_droid.touch_trackers(near.toArray(new Integer[0])); + } + + public boolean onMarkerClick(Marker marker) { + onMapClick(marker.getPosition()); + return true; } public void setupMap(int map_type) { @@ -170,6 +195,7 @@ public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarke 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)) -- cgit v1.2.3