/* * Copyright (C) 2010- Peer internet solutions * * This file is part of mixare. * * 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 3 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, see <http://www.gnu.org/licenses/> */ package org.mixare; import static android.view.KeyEvent.KEYCODE_CAMERA; import static android.view.KeyEvent.KEYCODE_DPAD_CENTER; import static android.view.KeyEvent.KEYCODE_DPAD_DOWN; import static android.view.KeyEvent.KEYCODE_DPAD_LEFT; import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT; import static android.view.KeyEvent.KEYCODE_DPAD_UP; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Timer; import java.util.TimerTask; import org.mixare.data.DataHandler; import org.mixare.data.DataSource; import org.mixare.gui.RadarPoints; import org.mixare.lib.MixUtils; import org.mixare.lib.gui.PaintScreen; import org.mixare.lib.gui.ScreenLine; import org.mixare.lib.marker.Marker; import org.mixare.lib.render.Camera; import org.mixare.mgr.downloader.DownloadManager; import org.mixare.mgr.downloader.DownloadRequest; import org.mixare.mgr.downloader.DownloadResult; import android.graphics.Color; import android.location.Location; import android.util.Log; import android.widget.Toast; /** * This class is able to update the markers and the radar. It also handles some * user events * * @author daniele * */ public class DataView { /** current context */ private MixContext mixContext; /** is the view Inited? */ private boolean isInit; /** width and height of the view */ private int width, height; /** * _NOT_ the android camera, the class that takes care of the transformation */ private Camera cam; private MixState state = new MixState(); /** The view can be "frozen" for debug purposes */ private boolean frozen; /** how many times to re-attempt download */ private int retry; private Location curFix; private DataHandler dataHandler = new DataHandler(); private float radius = 5; /** timer to refresh the browser */ private Timer refresh = null; private final long refreshDelay = 45 * 1000; // refresh every 45 seconds private boolean isLauncherStarted; private ArrayList<UIEvent> uiEvents = new ArrayList<UIEvent>(); private RadarPoints radarPoints = new RadarPoints(); private ScreenLine lrl = new ScreenLine(); private ScreenLine rrl = new ScreenLine(); private float rx = 10, ry = 20; private float addX = 0, addY = 0; private List<Marker> markers; /** * Constructor */ public DataView(MixContext ctx) { this.mixContext = ctx; } public MixContext getContext() { return mixContext; } public boolean isLauncherStarted() { return isLauncherStarted; } public boolean isFrozen() { return frozen; } public void setFrozen(boolean frozen) { this.frozen = frozen; } public float getRadius() { return radius; } public void setRadius(float radius) { this.radius = radius; } public DataHandler getDataHandler() { return dataHandler; } public boolean isDetailsView() { return state.isDetailsView(); } public void setDetailsView(boolean detailsView) { state.setDetailsView(detailsView); } public void doStart() { state.nextLStatus = MixState.NOT_STARTED; mixContext.getLocationFinder().setLocationAtLastDownload(curFix); } public boolean isInited() { return isInit; } public void init(int widthInit, int heightInit) { try { width = widthInit; height = heightInit; cam = new Camera(width, height, true); cam.setViewAngle(Camera.DEFAULT_VIEW_ANGLE); lrl.set(0, -RadarPoints.RADIUS); lrl.rotate(Camera.DEFAULT_VIEW_ANGLE / 2); lrl.add(rx + RadarPoints.RADIUS, ry + RadarPoints.RADIUS); rrl.set(0, -RadarPoints.RADIUS); rrl.rotate(-Camera.DEFAULT_VIEW_ANGLE / 2); rrl.add(rx + RadarPoints.RADIUS, ry + RadarPoints.RADIUS); } catch (Exception ex) { ex.printStackTrace(); } frozen = false; isInit = true; } public void requestData(String url) { DownloadRequest request = new DownloadRequest(new DataSource( "LAUNCHER", url, DataSource.TYPE.MIXARE, DataSource.DISPLAY.CIRCLE_MARKER, true)); mixContext.getDataSourceManager().setAllDataSourcesforLauncher( request.getSource()); mixContext.getDownloadManager().submitJob(request); state.nextLStatus = MixState.PROCESSING; } // public void requestData(DataSource datasource, double lat, double lon, double alt, float radius, String locale) { // DownloadRequest request = new DownloadRequest(); // request.params = datasource.createRequestParams(lat, lon, alt, radius, locale); // request.source = datasource; // // mixContext.getDownloadManager().submitJob(request); // state.nextLStatus = MixState.PROCESSING; // } public void draw(PaintScreen dw) { mixContext.getRM(cam.transform); curFix = mixContext.getLocationFinder().getCurrentLocation(); state.calcPitchBearing(cam.transform); // Load Layer if (state.nextLStatus == MixState.NOT_STARTED && !frozen) { loadDrawLayer(); markers = new ArrayList<Marker>(); } else if (state.nextLStatus == MixState.PROCESSING) { DownloadManager dm = mixContext.getDownloadManager(); DownloadResult dRes = null; markers.addAll(downloadDrawResults(dm, dRes)); if (dm.isDone()) { retry = 0; state.nextLStatus = MixState.DONE; dataHandler = new DataHandler(); dataHandler.addMarkers(markers); dataHandler.onLocationChanged(curFix); if (refresh == null) { // start the refresh timer if it is null refresh = new Timer(false); Date date = new Date(System.currentTimeMillis() + refreshDelay); refresh.schedule(new TimerTask() { @Override public void run() { callRefreshToast(); refresh(); } }, date, refreshDelay); } } } // Update markers dataHandler.updateActivationStatus(mixContext); for (int i = dataHandler.getMarkerCount() - 1; i >= 0; i--) { Marker ma = dataHandler.getMarker(i); // if (ma.isActive() && (ma.getDistance() / 1000f < radius || ma // instanceof NavigationMarker || ma instanceof SocialMarker)) { if (ma.isActive() && (ma.getDistance() / 1000f < radius)) { // To increase performance don't recalculate position vector // for every marker on every draw call, instead do this only // after onLocationChanged and after downloading new marker // if (!frozen) // ma.update(curFix); if (!frozen) ma.calcPaint(cam, addX, addY); ma.draw(dw); } } // Draw Radar drawRadar(dw); // Get next event UIEvent evt = null; synchronized (uiEvents) { if (uiEvents.size() > 0) { evt = uiEvents.get(0); uiEvents.remove(0); } } if (evt != null) { switch (evt.type) { case UIEvent.KEY: handleKeyEvent((KeyEvent) evt); break; case UIEvent.CLICK: handleClickEvent((ClickEvent) evt); break; } } state.nextLStatus = MixState.PROCESSING; } /** * Part of draw function, loads the layer. */ private void loadDrawLayer(){ if (mixContext.getStartUrl().length() > 0) { requestData(mixContext.getStartUrl()); isLauncherStarted = true; } else { double lat = curFix.getLatitude(), lon = curFix.getLongitude(), alt = curFix .getAltitude(); state.nextLStatus = MixState.PROCESSING; mixContext.getDataSourceManager().requestDataFromAllActiveDataSource(lat, lon, alt, radius); } // if no datasources are activated if (state.nextLStatus == MixState.NOT_STARTED) state.nextLStatus = MixState.DONE; } private List<Marker> downloadDrawResults(DownloadManager dm, DownloadResult dRes){ List<Marker> markers = new ArrayList<Marker>(); while ((dRes = dm.getNextResult()) != null) { if (dRes.isError() && retry < 3) { retry++; mixContext.getDownloadManager().submitJob( dRes.getErrorRequest()); // Notification // Toast.makeText(mixContext, dRes.errorMsg, // Toast.LENGTH_SHORT).show(); } if(!dRes.isError()) { if(dRes.getMarkers() != null){ //jLayer = (DataHandler) dRes.obj; Log.i(MixView.TAG,"Adding Markers"); markers.addAll(dRes.getMarkers()); // Notification Toast.makeText( mixContext, mixContext.getResources().getString( R.string.download_received) + " " + dRes.getDataSource().getName(), Toast.LENGTH_SHORT).show(); } } } return markers; } /** * Handles drawing radar and direction. * @param PaintScreen screen that radar will be drawn to */ private void drawRadar(PaintScreen dw) { String dirTxt = ""; int bearing = (int) state.getCurBearing(); int range = (int) (state.getCurBearing() / (360f / 16f)); // TODO: get strings from the values xml file if (range == 15 || range == 0) dirTxt = getContext().getString(R.string.N); else if (range == 1 || range == 2) dirTxt = getContext().getString(R.string.NE); else if (range == 3 || range == 4) dirTxt = getContext().getString(R.string.E); else if (range == 5 || range == 6) dirTxt = getContext().getString(R.string.SE); else if (range == 7 || range == 8) dirTxt = getContext().getString(R.string.S); else if (range == 9 || range == 10) dirTxt = getContext().getString(R.string.SW); else if (range == 11 || range == 12) dirTxt = getContext().getString(R.string.W); else if (range == 13 || range == 14) dirTxt = getContext().getString(R.string.NW); radarPoints.view = this; dw.paintObj(radarPoints, rx, ry, -state.getCurBearing(), 1); dw.setFill(false); dw.setColor(Color.argb(150, 0, 0, 220)); dw.paintLine(lrl.x, lrl.y, rx + RadarPoints.RADIUS, ry + RadarPoints.RADIUS); dw.paintLine(rrl.x, rrl.y, rx + RadarPoints.RADIUS, ry + RadarPoints.RADIUS); dw.setColor(Color.rgb(255, 255, 255)); dw.setFontSize(12); radarText(dw, MixUtils.formatDist(radius * 1000), rx + RadarPoints.RADIUS, ry + RadarPoints.RADIUS * 2 - 10, false); radarText(dw, "" + bearing + ((char) 176) + " " + dirTxt, rx + RadarPoints.RADIUS, ry - 5, true); } private void handleKeyEvent(KeyEvent evt) { /** Adjust marker position with keypad */ final float CONST = 10f; switch (evt.keyCode) { case KEYCODE_DPAD_LEFT: addX -= CONST; break; case KEYCODE_DPAD_RIGHT: addX += CONST; break; case KEYCODE_DPAD_DOWN: addY += CONST; break; case KEYCODE_DPAD_UP: addY -= CONST; break; case KEYCODE_DPAD_CENTER: frozen = !frozen; break; case KEYCODE_CAMERA: frozen = !frozen; break; // freeze the overlay with the camera button default: //if key is set, then ignore event break; } } boolean handleClickEvent(ClickEvent evt) { boolean evtHandled = false; // Handle event if (state.nextLStatus == MixState.DONE) { // the following will traverse the markers in ascending order (by // distance) the first marker that // matches triggers the event. //TODO handle collection of markers. (what if user wants the one at the back) for (int i = 0; i < dataHandler.getMarkerCount() && !evtHandled; i++) { Marker pm = dataHandler.getMarker(i); evtHandled = pm.fClick(evt.x, evt.y, mixContext, state); } } return evtHandled; } private void radarText(PaintScreen dw, String txt, float x, float y, boolean bg) { float padw = 4, padh = 2; float w = dw.getTextWidth(txt) + padw * 2; float h = dw.getTextAsc() + dw.getTextDesc() + padh * 2; if (bg) { dw.setColor(Color.rgb(0, 0, 0)); dw.setFill(true); dw.paintRect(x - w / 2, y - h / 2, w, h); dw.setColor(Color.rgb(255, 255, 255)); dw.setFill(false); dw.paintRect(x - w / 2, y - h / 2, w, h); } dw.paintText(padw + x - w / 2, padh + dw.getTextAsc() + y - h / 2, txt, false); } public void clickEvent(float x, float y) { synchronized (uiEvents) { uiEvents.add(new ClickEvent(x, y)); } } public void keyEvent(int keyCode) { synchronized (uiEvents) { uiEvents.add(new KeyEvent(keyCode)); } } public void clearEvents() { synchronized (uiEvents) { uiEvents.clear(); } } public void cancelRefreshTimer() { if (refresh != null) { refresh.cancel(); } } /** * Re-downloads the markers, and draw them on the map. */ public void refresh(){ state.nextLStatus = MixState.NOT_STARTED; } private void callRefreshToast(){ mixContext.getActualMixView().runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText( mixContext, mixContext.getResources() .getString(R.string.refreshing), Toast.LENGTH_SHORT).show(); } }); } } class UIEvent { public static final int CLICK = 0; public static final int KEY = 1; public int type; } class ClickEvent extends UIEvent { public float x, y; public ClickEvent(float x, float y) { this.type = CLICK; this.x = x; this.y = y; } @Override public String toString() { return "(" + x + "," + y + ")"; } } class KeyEvent extends UIEvent { public int keyCode; public KeyEvent(int keyCode) { this.type = KEY; this.keyCode = keyCode; } @Override public String toString() { return "(" + keyCode + ")"; } }