2 * Copyright © 2013 Mike Beattie <mike@ethernal.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
18 package org.altusmetrum.AltosDroid;
23 import org.altusmetrum.altoslib_7.*;
25 import android.app.Activity;
26 import android.graphics.*;
27 import android.os.Bundle;
28 import android.support.v4.app.Fragment;
29 import android.support.v4.app.FragmentTransaction;
30 import android.view.*;
31 import android.widget.*;
32 import android.location.Location;
33 import android.content.*;
35 class Rocket implements Comparable {
42 tab.draw_bitmap(position, tab.rocket_bitmap, tab.rocket_off_x, tab.rocket_off_y);
43 tab.draw_text(position, name, 0, 3*tab.rocket_bitmap.getHeight()/4);
46 void set_position(AltosLatLon position, long last_packet) {
47 this.position = position;
48 this.last_packet = last_packet;
51 Rocket(String name, TabMapOffline tab) {
56 public int compareTo(Object o) {
57 Rocket other = (Rocket) o;
59 long diff = last_packet - other.last_packet;
69 public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {
80 int pad_off_x, pad_off_y;
82 int rocket_off_x, rocket_off_y;
84 int here_off_x, here_off_y;
86 private boolean pad_set;
88 private TextView mDistanceView;
89 private TextView mBearingView;
90 private TextView mTargetLatitudeView;
91 private TextView mTargetLongitudeView;
92 private TextView mReceiverLatitudeView;
93 private TextView mReceiverLongitudeView;
94 private AltosMapView map_view;
96 private double mapAccuracy = -1;
98 int stroke_width = 20;
101 void draw_text(AltosLatLon lat_lon, String text, int off_x, int off_y) {
102 if (lat_lon != null && map != null && map.transform != null) {
103 AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
105 Rect bounds = new Rect();
106 paint.getTextBounds(text, 0, text.length(), bounds);
108 int width = bounds.right - bounds.left;
109 int height = bounds.bottom - bounds.top;
113 x = x - width / 2.0f - off_x;
114 y = y + height / 2.0f - off_y;
115 paint.setColor(0xff000000);
116 canvas.drawText(text, 0, text.length(), x, y, paint);
120 void draw_bitmap(AltosLatLon lat_lon, Bitmap bitmap, int off_x, int off_y) {
121 if (lat_lon != null && map != null && map.transform != null) {
122 AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
124 canvas.drawBitmap(bitmap, pt.x - off_x, pt.y - off_y, paint);
128 HashMap<Integer,Rocket> rockets = new HashMap<Integer,Rocket>();
130 /* AltosMapInterface */
132 static final int WHITE = 0xffffffff;
133 static final int RED = 0xffff0000;
134 static final int PINK = 0xffff8080;
135 static final int YELLOW= 0xffffff00;
136 static final int CYAN = 0xff00ffff;
137 static final int BLUE = 0xff0000ff;
138 static final int BLACK = 0xff000000;
140 public static final int stateColors[] = {
154 public AltosMapPath new_path() {
158 public AltosMapLine new_line() {
162 class MapImage implements AltosImage {
163 public Bitmap bitmap;
165 public void flush() {
166 if (bitmap != null) {
172 public MapImage(File file) {
173 bitmap = BitmapFactory.decodeFile(file.getPath());
177 public AltosImage load_image(File file) throws Exception {
178 return new MapImage(file);
181 class MapMark extends AltosMapMark {
182 public void paint(AltosMapTransform t) {
185 MapMark(double lat, double lon, int state) {
186 super(lat, lon, state);
190 public AltosMapMark new_mark(double lat, double lon, int state) {
191 return new MapMark(lat, lon, state);
194 class MapTile extends AltosMapTile {
195 public void paint(AltosMapTransform t) {
196 AltosPointInt pt = new AltosPointInt(t.screen(upper_left));
198 if (canvas.quickReject(pt.x, pt.y, pt.x + px_size, pt.y + px_size, Canvas.EdgeType.AA))
201 AltosImage altos_image = cache.get(this, store, px_size, px_size);
203 MapImage map_image = (MapImage) altos_image;
205 Bitmap bitmap = null;
207 if (map_image != null)
208 bitmap = map_image.bitmap;
210 if (bitmap != null) {
211 canvas.drawBitmap(bitmap, pt.x, pt.y, paint);
213 paint.setColor(0xff808080);
214 canvas.drawRect(pt.x, pt.y, pt.x + px_size, pt.y + px_size, paint);
215 if (t.has_location()) {
216 String message = null;
218 case AltosMapTile.loading:
219 message = "Loading...";
221 case AltosMapTile.bad_request:
222 message = "Internal error";
224 case AltosMapTile.failed:
225 message = "Network error, check connection";
227 case AltosMapTile.forbidden:
228 message = "Too many requests, try later";
231 if (message != null) {
232 Rect bounds = new Rect();
233 paint.getTextBounds(message, 0, message.length(), bounds);
235 int width = bounds.right - bounds.left;
236 int height = bounds.bottom - bounds.top;
238 float x = pt.x + px_size / 2.0f;
239 float y = pt.y + px_size / 2.0f;
240 x = x - width / 2.0f;
241 y = y + height / 2.0f;
242 paint.setColor(0xff000000);
243 canvas.drawText(message, 0, message.length(), x, y, paint);
249 public MapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
250 super(listener, upper_left, center, zoom, maptype, px_size, 2);
255 public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
256 return new MapTile(listener, upper_left, center, zoom, maptype, px_size);
260 if (map_view != null)
261 return map_view.getWidth();
265 public int height() {
266 if (map_view != null)
267 return map_view.getHeight();
271 public void repaint() {
272 if (map_view != null)
273 map_view.postInvalidate();
276 public void repaint(AltosRectangle damage) {
277 if (map_view != null)
278 map_view.postInvalidate(damage.x, damage.y, damage.x + damage.width, damage.y + damage.height);
281 public void set_zoom_label(String label) {
284 public void debug(String format, Object ... arguments) {
285 AltosDebug.debug(format, arguments);
289 public void onAttach(Activity activity) {
290 super.onAttach(activity);
292 map = new AltosMap(this);
293 map.set_maptype(altos_droid.map_type);
295 pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad);
296 /* arrow at the bottom of the launchpad image */
297 pad_off_x = pad_bitmap.getWidth() / 2;
298 pad_off_y = pad_bitmap.getHeight();
300 rocket_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket);
301 /* arrow at the bottom of the rocket image */
302 rocket_off_x = rocket_bitmap.getWidth() / 2;
303 rocket_off_y = rocket_bitmap.getHeight();
305 here_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_maps_indicator_current_position);
306 /* Center of the dot */
307 here_off_x = here_bitmap.getWidth() / 2;
308 here_off_y = here_bitmap.getHeight() / 2;
312 public void onCreate(Bundle savedInstanceState) {
313 super.onCreate(savedInstanceState);
317 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
318 View v = inflater.inflate(R.layout.tab_map_offline, container, false);
320 map_view = (AltosMapView)v.findViewById(R.id.map_view_offline);
321 map_view.set_tab(this);
322 mDistanceView = (TextView)v.findViewById(R.id.distance_value_offline);
323 mBearingView = (TextView)v.findViewById(R.id.bearing_value_offline);
324 mTargetLatitudeView = (TextView)v.findViewById(R.id.target_lat_value_offline);
325 mTargetLongitudeView = (TextView)v.findViewById(R.id.target_lon_value_offline);
326 mReceiverLatitudeView = (TextView)v.findViewById(R.id.receiver_lat_value_offline);
327 mReceiverLongitudeView = (TextView)v.findViewById(R.id.receiver_lon_value_offline);
332 public void onActivityCreated(Bundle savedInstanceState) {
333 super.onActivityCreated(savedInstanceState);
337 public void onDestroyView() {
338 super.onDestroyView();
342 private void center(double lat, double lon, double accuracy) {
343 if (mapAccuracy < 0 || accuracy < mapAccuracy/10) {
345 map.maybe_centre(lat, lon);
346 mapAccuracy = accuracy;
350 public String tab_name() { return "offmap"; }
352 public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
353 if (from_receiver != null) {
354 mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
355 set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
359 map.show(state, null);
360 if (state.gps != null) {
361 mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
362 mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
363 if (state.gps.locked && state.gps.nsat >= 4)
364 center (state.gps.lat, state.gps.lon, 10);
366 if (state.pad_lat != AltosLib.MISSING && pad == null)
367 pad = new AltosLatLon(state.pad_lat, state.pad_lon);
370 if (telem_state != null) {
371 Integer[] old_serial = rockets.keySet().toArray(new Integer[0]);
372 Integer[] new_serial = telem_state.states.keySet().toArray(new Integer[0]);
374 /* remove deleted keys */
375 for (int serial : old_serial) {
376 if (!telem_state.states.containsKey(serial))
377 rockets.remove(serial);
380 /* set remaining keys */
382 for (int serial : new_serial) {
384 AltosState t_state = telem_state.states.get(serial);
385 if (rockets.containsKey(serial))
386 rocket = rockets.get(serial);
388 rocket = new Rocket(String.format("%d", serial), this);
389 rockets.put(serial, rocket);
391 if (t_state.gps != null)
392 rocket.set_position(new AltosLatLon(t_state.gps.lat, t_state.gps.lon), t_state.received_time);
396 if (receiver != null) {
399 here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
400 if (receiver.hasAccuracy())
401 accuracy = receiver.getAccuracy();
404 mReceiverLatitudeView.setText(AltosDroid.pos(here.lat, "N", "S"));
405 mReceiverLongitudeView.setText(AltosDroid.pos(here.lon, "E", "W"));
406 center (here.lat, here.lon, accuracy);
412 public void set_map_type(int map_type) {
414 map.set_maptype(map_type);
417 public TabMapOffline() {