com.byagowi.persiancalendar.view.QiblaCompassView.java Source code

Java tutorial

Introduction

Here is the source code for com.byagowi.persiancalendar.view.QiblaCompassView.java

Source

// copyedited from https://code.google.com/p/android-salat-times/source/browse/src/com/cepmuvakkit/times/view/QiblaCompassView.java
// licensed under GPLv3
package com.byagowi.persiancalendar.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.View;

import com.byagowi.persiancalendar.R;
import com.cepmuvakkit.times.posAlgo.AstroLib;
import com.cepmuvakkit.times.posAlgo.EarthHeading;
import com.cepmuvakkit.times.posAlgo.Horizontal;
import com.cepmuvakkit.times.posAlgo.SunMoonPosition;

import java.util.GregorianCalendar;

public class QiblaCompassView extends View {
    private Paint dashedPaint;
    private int px, py; // Center of Compass (px,py)
    private int Radius; // Radius of Compass dial
    private int r; // Radius of Sun and Moon
    private String northString, eastString, southString, westString;
    private DashPathEffect dashPath;
    private float bearing;
    private Horizontal sunPosition, moonPosition;
    private EarthHeading qiblaInfo;
    private SunMoonPosition sunMoonPosition;
    private double longitude = 0.0;
    private double latitude = 0.0;

    private Paint textPaint;

    public QiblaCompassView(Context context) {
        super(context);
        initCompassView();
    }

    public QiblaCompassView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initCompassView();
    }

    public QiblaCompassView(Context context, AttributeSet ats, int defaultStyle) {
        super(context, ats, defaultStyle);
        initCompassView();
    }

private void initAstronomicParameters() {
    GregorianCalendar c = new GregorianCalendar();
    double jd = AstroLib.calculateJulianDay(c);

    double T = 0;
    double altitude = 0.0;
    sunMoonPosition = new SunMoonPosition(jd, latitude, longitude,
            altitude, T);
    sunPosition = sunMoonPosition.getSunPosition();
    moonPosition = sunMoonPosition.getMoonPosition();
}

    public void initCompassView() {
        setFocusable(true);
        initAstronomicParameters();
        northString = "N";
        eastString = "E";
        southString = "S";
        westString = "W";

        dashPath = new DashPathEffect(new float[] { 2, 5 }, 1);
        dashedPaint = new Paint(Paint.FAKE_BOLD_TEXT_FLAG);
        dashedPaint.setPathEffect(dashPath);
        dashedPaint.setStrokeWidth(2);
        dashedPaint.setPathEffect(dashPath);
        dashedPaint.setColor(ContextCompat.getColor(getContext(), R.color.qibla_color));

        textPaint = new Paint(Paint.FAKE_BOLD_TEXT_FLAG);
        textPaint.setColor(ContextCompat.getColor(getContext(), (R.color.qibla_color)));
        textPaint.setTextSize(20);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // The compass is a circle that fills as much space as possible.
        // Set the measured dimensions by figuring out the shortest boundary,
        // height or width.
        int measuredWidth = measure(widthMeasureSpec);
        int measuredHeight = measure(heightMeasureSpec);

        // int d = Math.min(measuredWidth, measuredHeight);

        setMeasuredDimension(measuredWidth, measuredHeight);
    }

    private int measure(int measureSpec) {
        int result;

        // Decode the measurement specifications.
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.UNSPECIFIED) {
            // Return a default size of 200 if no bounds are specified.
            result = 600;
        } else {
            // As you want to fill the available space
            // always return the full available bounds.
            result = specSize;
        }
        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        this.Radius = Math.min(px, py);
        this.r = Radius / 10; // Sun Moon radius;
        // over here
        qiblaInfo = sunMoonPosition.getDestinationHeading();
        textPaint.setTextAlign(Paint.Align.LEFT);
        textPaint.setColor(ContextCompat.getColor(getContext(), (R.color.qibla_color)));
        canvas.rotate(-bearing, px, py);// Attach and Detach capability lies
        canvas.save();
        drawDial(canvas);
        if (isLongLatAvailable()) {
            canvas.save();
            drawQibla(canvas);
        }
        canvas.save();
        drawTrueNorthArrow(canvas, bearing);
        if (isLongLatAvailable()) {
            canvas.save();
            drawMoon(canvas);
            canvas.save();
            drawSun(canvas);
        }
        canvas.save();
    }

    public boolean isLongLatAvailable() {
        return longitude != 0.0 && latitude != 0.0;
    }

    Path mPath = new Path();
    Paint trueNorthArrowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    public void drawTrueNorthArrow(Canvas canvas, float drawnAngle) {
        trueNorthArrowPaint.reset();
        trueNorthArrowPaint.setColor(Color.RED);
        trueNorthArrowPaint.setStyle(Paint.Style.FILL);
        trueNorthArrowPaint.setAlpha(100);
        int r = Radius / 12;
        // Construct a wedge-shaped path
        mPath.reset();
        mPath.moveTo(px, py - px);
        mPath.lineTo(px - r, py);
        mPath.lineTo(px, py + r);
        mPath.lineTo(px + r, py);
        mPath.addCircle(px, py, r, Path.Direction.CCW);
        mPath.close();
        canvas.drawPath(mPath, trueNorthArrowPaint);
        dashedPaint.setColor(Color.RED);
        canvas.drawLine(px, py - px, px, py + Radius, dashedPaint);
        canvas.drawCircle(px, py, 5, dashedPaint);
        canvas.restore();
    }

    Paint markerPaint = new Paint(Paint.FAKE_BOLD_TEXT_FLAG);
    Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    public void drawDial(Canvas canvas) {
        // over here
        circlePaint.reset();
        circlePaint.setColor(ContextCompat.getColor(getContext(), R.color.qibla_color));
        circlePaint.setStrokeWidth(1);
        circlePaint.setStyle(Paint.Style.STROKE); // Sadece Cember ciziyor.

        int textHeight = (int) textPaint.measureText("yY");
        markerPaint.reset();
        markerPaint.setColor(ContextCompat.getColor(getContext(), R.color.qibla_color));
        // Draw the background
        canvas.drawCircle(px, py, Radius, circlePaint);
        canvas.drawCircle(px, py, Radius - 20, circlePaint);
        // Rotate our perspective so that the "top" is
        // facing the current bearing.

        int textWidth = (int) textPaint.measureText("W");
        int cardinalX = px - textWidth / 2;
        int cardinalY = py - Radius + textHeight;

        // Draw the marker every 15 degrees and text every 45.
        for (int i = 0; i < 24; i++) {
            // Draw a marker.
            canvas.drawLine(px, py - Radius, px, py - Radius + 10, markerPaint);
            canvas.save();
            canvas.translate(0, textHeight);
            // Draw the cardinal points
            if (i % 6 == 0) {
                String dirString = "";
                switch (i) {
                case (0): {
                    dirString = northString;
                    break;
                }
                case (6):
                    dirString = eastString;
                    break;
                case (12):
                    dirString = southString;
                    break;
                case (18):
                    dirString = westString;
                    break;
                }
                canvas.drawText(dirString, cardinalX, cardinalY, textPaint);
            } else if (i % 3 == 0) {
                // Draw the text every alternate 45deg
                String angle = String.valueOf(i * 15);
                float angleTextWidth = textPaint.measureText(angle);
                int angleTextX = (int) (px - angleTextWidth / 2);
                int angleTextY = py - Radius + textHeight;
                canvas.drawText(angle, angleTextX, angleTextY, textPaint);
            }
            canvas.restore();

            canvas.rotate(15, px, py);
        }

    }

    Paint sunPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    public void drawSun(Canvas canvas) {
        sunPaint.reset();
        sunPaint.setColor(Color.YELLOW);
        sunPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        // Horizontal sunPosition = new Horizontal(225, 45);

        if (sunPosition.getElevation() > -10) {
            canvas.rotate((float) sunPosition.getAzimuth() - 360, px, py);
            sunPaint.setPathEffect(dashPath);

            int ry = (int) (((90 - sunPosition.getElevation()) / 90) * Radius);
            canvas.drawCircle(px, py - ry, r, sunPaint);
            dashedPaint.setColor(Color.YELLOW);
            canvas.drawLine(px, py - Radius, px, py + Radius, dashedPaint);
            sunPaint.setPathEffect(null);
            canvas.restore();
        }

    }

    Paint moonPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Paint moonPaintB = new Paint(Paint.ANTI_ALIAS_FLAG);
    Paint moonPaintO = new Paint(Paint.ANTI_ALIAS_FLAG);
    Paint moonPaintD = new Paint(Paint.ANTI_ALIAS_FLAG);
    RectF moonRect = new RectF();
    RectF moonOval = new RectF();

    public void drawMoon(Canvas canvas) {
        moonPaint.reset();
        moonPaint.setColor(Color.WHITE);
        moonPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        moonPaintB.reset();// moon Paint Black
        moonPaintB.setColor(Color.BLACK);
        moonPaintB.setStyle(Paint.Style.FILL_AND_STROKE);
        moonPaintO.reset();// moon Paint for Oval
        moonPaintO.setColor(Color.WHITE);
        moonPaintO.setStyle(Paint.Style.FILL_AND_STROKE);
        moonPaintD.reset();// moon Paint for Diameter
        // draw
        moonPaintD.setColor(Color.GRAY);
        moonPaintD.setStyle(Paint.Style.STROKE);
        double moonPhase = sunMoonPosition.getMoonPhase();
        if (moonPosition.getElevation() > -5) {
            canvas.rotate((float) moonPosition.getAzimuth() - 360, px, py);
            int eOffset = (int) ((moonPosition.getElevation() / 90) * Radius);
            // elevation Offset 0 for 0 degree; r for 90 degree
            moonRect.set(px - r, py + eOffset - Radius - r, px + r, py + eOffset - Radius + r);
            canvas.drawArc(moonRect, 90, 180, false, moonPaint);
            canvas.drawArc(moonRect, 270, 180, false, moonPaintB);
            int arcWidth = (int) ((moonPhase - 0.5) * (4 * r));
            moonPaintO.setColor(arcWidth < 0 ? Color.BLACK : Color.WHITE);
            moonOval.set(px - Math.abs(arcWidth) / 2, py + eOffset - Radius - r, px + Math.abs(arcWidth) / 2,
                    py + eOffset - Radius + r);
            canvas.drawArc(moonOval, 0, 360, false, moonPaintO);
            canvas.drawArc(moonRect, 0, 360, false, moonPaintD);
            moonPaintD.setPathEffect(dashPath);
            canvas.drawLine(px, py - Radius, px, py + Radius, moonPaintD);
            moonPaintD.setPathEffect(null);
            canvas.restore();

        }

    }

    Paint qiblaPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Bitmap kaaba = BitmapFactory.decodeResource(getResources(), R.drawable.kaaba);

    public void drawQibla(Canvas canvas) {

        canvas.rotate((float) qiblaInfo.getHeading() - 360, px, py);
        qiblaPaint.reset();
        qiblaPaint.setColor(Color.GREEN);
        qiblaPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        qiblaPaint.setPathEffect(dashPath);
        qiblaPaint.setStrokeWidth(5.5f);

        canvas.drawLine(px, py - Radius, px, py + Radius, qiblaPaint);
        qiblaPaint.setPathEffect(null);
        canvas.drawBitmap(kaaba, px - kaaba.getWidth() / 2, py - Radius - kaaba.getHeight() / 2, qiblaPaint);
        canvas.restore();

    }

    public void setBearing(float _bearing) {
        bearing = _bearing;
    }

    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }

    public void setScreenResolution(int widthPixels, int heightPixels) {
        this.px = widthPixels / 2;
        this.py = heightPixels / 2;
    }
}