aboutsummaryrefslogtreecommitdiffstats
path: root/main/src/cgeo/geocaching/ui/CompassView.java
diff options
context:
space:
mode:
authorBananeweizen <bananeweizen@gmx.de>2012-01-14 15:02:15 +0100
committerBananeweizen <bananeweizen@gmx.de>2012-01-14 15:02:15 +0100
commite61a40fc8a4a3d5ab5a59346eddd1c43bcc4857e (patch)
treea2a8aec422fde537c500cf90ad70e606ba416121 /main/src/cgeo/geocaching/ui/CompassView.java
parent77a8bc851569ae7a82f76a0913b39006074ac56a (diff)
downloadcgeo-e61a40fc8a4a3d5ab5a59346eddd1c43bcc4857e.zip
cgeo-e61a40fc8a4a3d5ab5a59346eddd1c43bcc4857e.tar.gz
cgeo-e61a40fc8a4a3d5ab5a59346eddd1c43bcc4857e.tar.bz2
refactoring: move more widget related classes into own package
Diffstat (limited to 'main/src/cgeo/geocaching/ui/CompassView.java')
-rw-r--r--main/src/cgeo/geocaching/ui/CompassView.java291
1 files changed, 291 insertions, 0 deletions
diff --git a/main/src/cgeo/geocaching/ui/CompassView.java b/main/src/cgeo/geocaching/ui/CompassView.java
new file mode 100644
index 0000000..20495c0
--- /dev/null
+++ b/main/src/cgeo/geocaching/ui/CompassView.java
@@ -0,0 +1,291 @@
+package cgeo.geocaching.ui;
+
+import cgeo.geocaching.R;
+import cgeo.geocaching.Settings;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+
+public class CompassView extends View {
+
+ private changeThread watchdog = null;
+ private volatile boolean wantStop = false;
+ private Context context = null;
+ private Bitmap compassUnderlay = null;
+ private Bitmap compassRose = null;
+ private Bitmap compassArrow = null;
+ private Bitmap compassOverlay = null;
+ /**
+ * North direction currently SHOWN on compass (not measured)
+ */
+ private double azimuthShown = 0.0;
+ /**
+ * cache direction currently SHOWN on compass (not measured)
+ */
+ private double cacheHeadingShown = 0.0;
+ /**
+ * cache direction measured from device, or 0.0
+ */
+ private double cacheHeadingMeasured = 0.0;
+ /**
+ * North direction measured from device, or 0.0
+ */
+ private double northMeasured = 0.0;
+ private PaintFlagsDrawFilter setfil = null;
+ private PaintFlagsDrawFilter remfil = null;
+ private int compassUnderlayWidth = 0;
+ private int compassUnderlayHeight = 0;
+ private int compassRoseWidth = 0;
+ private int compassRoseHeight = 0;
+ private int compassArrowWidth = 0;
+ private int compassArrowHeight = 0;
+ private int compassOverlayWidth = 0;
+ private int compassOverlayHeight = 0;
+ private boolean initialDisplay;
+ private Handler changeHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message message) {
+ try {
+ invalidate();
+ } catch (Exception e) {
+ Log.e(Settings.tag, "CompassView.changeHandler: " + e.toString());
+ }
+ }
+ };
+
+ public CompassView(Context contextIn) {
+ super(contextIn);
+ context = contextIn;
+ }
+
+ public CompassView(Context contextIn, AttributeSet attrs) {
+ super(contextIn, attrs);
+ context = contextIn;
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ compassUnderlay = BitmapFactory.decodeResource(context.getResources(), R.drawable.compass_underlay);
+ compassRose = BitmapFactory.decodeResource(context.getResources(), R.drawable.compass_rose);
+ compassArrow = BitmapFactory.decodeResource(context.getResources(), R.drawable.compass_arrow);
+ compassOverlay = BitmapFactory.decodeResource(context.getResources(), R.drawable.compass_overlay);
+
+ compassUnderlayWidth = compassUnderlay.getWidth();
+ compassUnderlayHeight = compassUnderlay.getWidth();
+ compassRoseWidth = compassRose.getWidth();
+ compassRoseHeight = compassRose.getWidth();
+ compassArrowWidth = compassArrow.getWidth();
+ compassArrowHeight = compassArrow.getWidth();
+ compassOverlayWidth = compassOverlay.getWidth();
+ compassOverlayHeight = compassOverlay.getWidth();
+
+ setfil = new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG);
+ remfil = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0);
+
+ initialDisplay = true;
+ wantStop = false;
+
+ watchdog = new changeThread();
+ watchdog.start();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ wantStop = true;
+
+ if (compassUnderlay != null) {
+ compassUnderlay.recycle();
+ }
+
+ if (compassRose != null) {
+ compassRose.recycle();
+ }
+
+ if (compassArrow != null) {
+ compassArrow.recycle();
+ }
+
+ if (compassOverlay != null) {
+ compassOverlay.recycle();
+ }
+ }
+
+ public synchronized void updateNorth(double northHeadingIn, double cacheHeadingIn) {
+ if (initialDisplay) {
+ // We will force the compass to move brutally if this is the first
+ // update since it is visible.
+ azimuthShown = northHeadingIn;
+ cacheHeadingShown = cacheHeadingIn;
+
+ // it may take some time to get an initial direction measurement for the device
+ if (northHeadingIn != 0.0) {
+ initialDisplay = false;
+ }
+ }
+ northMeasured = northHeadingIn;
+ cacheHeadingMeasured = cacheHeadingIn;
+ }
+
+ /**
+ * Compute the new value, moving by small increments.
+ *
+ * @param goal
+ * the goal to reach
+ * @param actual
+ * the actual value
+ * @return the new value
+ */
+ static protected double smoothUpdate(double goal, double actual) {
+ double diff = goal - actual;
+ final boolean largeDiff = Math.abs(diff) > 5;
+
+ double offset = 0.0;
+
+ if (diff < 0.0) {
+ diff += 360.0;
+ } else if (diff >= 360.0) {
+ diff -= 360.0;
+ }
+
+ // If the difference is smaller than 1 degree, do nothing as it
+ // causes the arrow to vibrate.
+ if (diff > 1.0 && diff <= 180.0) {
+ offset = largeDiff ? 2.0 : 1.0;
+ } else if (diff > 180.0 && diff < 359.0) {
+ offset = largeDiff ? -2.0 : -1.0;
+ }
+
+ return actual + offset;
+ }
+
+ private class changeThread extends Thread {
+
+ @Override
+ public void run() {
+ while (!wantStop) {
+ try {
+ sleep(50);
+ } catch (Exception e) {
+ // nothing
+ }
+
+ synchronized (CompassView.this) {
+ azimuthShown = smoothUpdate(northMeasured, azimuthShown);
+ cacheHeadingShown = smoothUpdate(cacheHeadingMeasured, cacheHeadingShown);
+ }
+
+ changeHandler.sendMessage(new Message());
+ }
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ // use local synchronized variables to avoid them being changed from the device during drawing
+ double azimuthDrawn;
+ double headingDrawn;
+
+ synchronized (this) {
+ azimuthDrawn = azimuthShown;
+ headingDrawn = cacheHeadingShown;
+ }
+
+ double azimuthTemp = azimuthDrawn;
+ double azimuthRelative = azimuthTemp - headingDrawn;
+ if (azimuthRelative < 0) {
+ azimuthRelative += 360;
+ } else if (azimuthRelative >= 360) {
+ azimuthRelative -= 360;
+ }
+
+ // compass margins
+ int canvasCenterX = (compassRoseWidth / 2) + ((getWidth() - compassRoseWidth) / 2);
+ int canvasCenterY = (compassRoseHeight / 2) + ((getHeight() - compassRoseHeight) / 2);
+
+ int marginLeftTemp = 0;
+ int marginTopTemp = 0;
+
+ super.onDraw(canvas);
+
+ canvas.save();
+ canvas.setDrawFilter(setfil);
+
+ marginLeftTemp = (getWidth() - compassUnderlayWidth) / 2;
+ marginTopTemp = (getHeight() - compassUnderlayHeight) / 2;
+
+ canvas.drawBitmap(compassUnderlay, marginLeftTemp, marginTopTemp, null);
+
+ marginLeftTemp = (getWidth() - compassRoseWidth) / 2;
+ marginTopTemp = (getHeight() - compassRoseHeight) / 2;
+
+ canvas.rotate((float) -azimuthTemp, canvasCenterX, canvasCenterY);
+ canvas.drawBitmap(compassRose, marginLeftTemp, marginTopTemp, null);
+ canvas.rotate((float) azimuthTemp, canvasCenterX, canvasCenterY);
+
+ marginLeftTemp = (getWidth() - compassArrowWidth) / 2;
+ marginTopTemp = (getHeight() - compassArrowHeight) / 2;
+
+ canvas.rotate((float) -azimuthRelative, canvasCenterX, canvasCenterY);
+ canvas.drawBitmap(compassArrow, marginLeftTemp, marginTopTemp, null);
+ canvas.rotate((float) azimuthRelative, canvasCenterX, canvasCenterY);
+
+ marginLeftTemp = (getWidth() - compassOverlayWidth) / 2;
+ marginTopTemp = (getHeight() - compassOverlayHeight) / 2;
+
+ canvas.drawBitmap(compassOverlay, marginLeftTemp, marginTopTemp, null);
+
+ canvas.setDrawFilter(remfil);
+ canvas.restore();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
+ }
+
+ private int measureWidth(int measureSpec) {
+ int result = 0;
+ int specMode = MeasureSpec.getMode(measureSpec);
+ int specSize = MeasureSpec.getSize(measureSpec);
+
+ if (specMode == MeasureSpec.EXACTLY) {
+ result = specSize;
+ } else {
+ result = compassArrow.getWidth() + getPaddingLeft() + getPaddingRight();
+
+ if (specMode == MeasureSpec.AT_MOST) {
+ result = Math.min(result, specSize);
+ }
+ }
+
+ return result;
+ }
+
+ private int measureHeight(int measureSpec) {
+ int result = 0;
+ int specMode = MeasureSpec.getMode(measureSpec);
+ int specSize = MeasureSpec.getSize(measureSpec);
+
+ if (specMode == MeasureSpec.EXACTLY) {
+ result = specSize;
+ } else {
+ result = compassArrow.getHeight() + getPaddingTop() + getPaddingBottom();
+
+ if (specMode == MeasureSpec.AT_MOST) {
+ result = Math.min(result, specSize);
+ }
+ }
+
+ return result;
+ }
+} \ No newline at end of file