com.forrestguice.suntimeswidget.LightMapView.java Source code

Java tutorial

Introduction

Here is the source code for com.forrestguice.suntimeswidget.LightMapView.java

Source

/**
Copyright (C) 2014 Forrest Guice
This file is part of SuntimesWidget.
    
SuntimesWidget 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.
    
SuntimesWidget 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 SuntimesWidget.  If not, see <http://www.gnu.org/licenses/>.
*/
package com.forrestguice.suntimeswidget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;

import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;

import android.widget.ImageView;
import android.widget.LinearLayout;

import com.forrestguice.suntimeswidget.calculator.SuntimesRiseSetData;
import com.forrestguice.suntimeswidget.calculator.SuntimesRiseSetDataset;

import java.util.Calendar;

/**
 * LightMapView .. a stacked bar graph over the duration of a day showing relative duration of
 * night, day, and twilight times.
 */
public class LightMapView extends LinearLayout {
    private static final double MINUTES_IN_DAY = 24 * 60;

    public static final int DEFAULT_MAX_UPDATE_RATE = 15 * 1000; // ms value; once every 15s
    public static final int DEFAULT_POINT_RADIUS = 3;
    public static final int DEFAULT_STROKE_WIDTH = 2;
    public static final int DEFAULT_WIDTH = 288;
    public static final int DEFAULT_HEIGHT = 16;

    private int maxUpdateRate = DEFAULT_MAX_UPDATE_RATE;
    private int pointRadius = DEFAULT_POINT_RADIUS;
    private int pointStrokeWidth = DEFAULT_STROKE_WIDTH;
    private int colorNight, colorAstro, colorNautical, colorCivil, colorDay, colorPointFill, colorPointStroke;

    private ImageView mainView;
    private SuntimesRiseSetDataset data = null;
    private long lastUpdate = 0;
    private boolean resizable = true;

    public LightMapView(Context context) {
        super(context);
        initLayout(context);
        initViews(context);
    }

    public LightMapView(Context context, AttributeSet attribs) {
        super(context, attribs);
        initLayout(context);
        initViews(context);
    }

    /**
     * @param context a context used to access resources
     */
    private void initLayout(Context context) {
        final LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.info_time_lightmap, this);
    }

    /**
     * @param context a context used to access resources
     */
    protected void initViews(Context context) {
        mainView = (ImageView) findViewById(R.id.lightmap_view);
        initColors(context);
    }

    /**
     * @param context a context used to access resources
     */
    @SuppressWarnings("ResourceType")
    private void initColors(Context context) {
        int[] colorAttrs = { R.attr.graphColor_night, // 0
                R.attr.graphColor_astronomical, // 1
                R.attr.graphColor_nautical, // 2
                R.attr.graphColor_civil, // 3
                R.attr.graphColor_day, // 4
                R.attr.graphColor_pointFill, // 5
                R.attr.graphColor_pointStroke }; // 6
        TypedArray typedArray = context.obtainStyledAttributes(colorAttrs);
        int def = R.color.color_transparent;

        colorNight = ContextCompat.getColor(context, typedArray.getResourceId(0, def));
        colorAstro = ContextCompat.getColor(context, typedArray.getResourceId(1, def));
        colorNautical = ContextCompat.getColor(context, typedArray.getResourceId(2, def));
        colorCivil = ContextCompat.getColor(context, typedArray.getResourceId(3, def));
        colorDay = ContextCompat.getColor(context, typedArray.getResourceId(4, def));
        colorPointFill = ContextCompat.getColor(context, typedArray.getResourceId(5, def));
        colorPointStroke = ContextCompat.getColor(context, typedArray.getResourceId(6, def));

        typedArray.recycle();
    }

    public int getMaxUpdateRate() {
        return maxUpdateRate;
    }

    public void setResizable(boolean value) {
        resizable = value;
    }

    /**
     *
     */
    public void onResume() {
        Log.d("DEBUG", "LightMapView onResume");
    }

    /**
     * @param w the changed width
     * @param h the changed height
     * @param oldw the previous width
     * @param oldh the previous height
     */
    @Override
    public void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (resizable) {
            updateViews(true);
        }
    }

    /**
     * throttled update method
     */
    public void updateViews(boolean forceUpdate) {
        long timeSinceLastUpdate = (System.currentTimeMillis() - lastUpdate);
        if (forceUpdate || timeSinceLastUpdate >= maxUpdateRate) {
            updateViews(data);
        }
    }

    /**
     * @param data an instance of SuntimesRiseSetDataset
     */
    public void updateViews(SuntimesRiseSetDataset data) {
        this.data = data;
        int w = getWidth();
        int h = getHeight();

        if (mainView != null && w > 0 && h > 0) {
            long bench_start = System.nanoTime();
            Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
            Canvas c = new Canvas(b);
            Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

            boolean layer_astro, layer_nautical, layer_civil;

            // draw background (night)
            p.setColor(colorNight);
            drawRect(c, p);

            if (data != null) {
                // draw astro twilight
                p.setColor(colorAstro);
                if (!(layer_astro = drawRect(data.dataAstro, c, p))) {
                    if (data.dataNautical.hasSunriseTimeToday() || data.dataNautical.hasSunsetTimeToday()) {
                        drawRect(c, p);
                    }
                }

                // draw nautical twilight
                p.setColor(colorNautical);
                if (!(layer_nautical = drawRect(data.dataNautical, c, p))) {
                    if (data.dataCivil.hasSunriseTimeToday() || data.dataCivil.hasSunsetTimeToday()) {
                        drawRect(c, p);
                    }
                }

                // draw civil twilight
                p.setColor(colorCivil);
                if (!(layer_civil = drawRect(data.dataCivil, c, p))) {
                    if (data.dataActual.hasSunriseTimeToday() || data.dataActual.hasSunsetTimeToday()) {
                        drawRect(c, p);
                    }
                }

                // draw foreground (day)
                p.setColor(colorDay);
                if (!drawRect(data.dataActual, c, p)) {
                    boolean noLayers = !layer_astro && !layer_nautical && !layer_civil;
                    if (noLayers && data.isDay()) {
                        drawRect(c, p);
                    }
                }

                // draw now marker
                drawPoint(data.now(), pointRadius, c, p);
            }

            mainView.setImageBitmap(b);
            lastUpdate = System.currentTimeMillis();
            long bench_end = System.nanoTime();
            Log.d("DEBUG", "lightmap updated in " + ((bench_end - bench_start) / 1000000.0) + "ms :: " + w + "," + h
                    + " :: hasView: " + (mainView != null) + " :: hasData " + (data != null));
        }
    }

    private void drawRect(Canvas c, Paint p) {
        int w = c.getWidth();
        int h = c.getHeight();
        c.drawRect(0, 0, w, h, p);
    }

    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    private boolean drawRect(SuntimesRiseSetData data, Canvas c, Paint p) {
        Calendar riseTime = data.sunriseCalendarToday();
        Calendar setTime = data.sunsetCalendarToday();
        if (riseTime == null && setTime == null) {
            return false;
        }

        int w = c.getWidth();
        int h = c.getHeight();

        int left = 0;
        if (riseTime != null) {
            double riseMinute = riseTime.get(Calendar.HOUR_OF_DAY) * 60 + riseTime.get(Calendar.MINUTE);
            double riseR = riseMinute / MINUTES_IN_DAY;
            left = (int) Math.round(riseR * w);
        }

        int right = w;
        if (setTime != null) {
            double setMinute = setTime.get(Calendar.HOUR_OF_DAY) * 60 + setTime.get(Calendar.MINUTE);
            double setR = setMinute / MINUTES_IN_DAY;
            right = (int) Math.round(setR * w);
        }

        boolean setTimeBeforeRiseTime = (riseTime != null && setTime != null
                && setTime.getTime().before(riseTime.getTime()));
        if (setTimeBeforeRiseTime) {
            c.drawRect(0, 0, right, h, p);
            c.drawRect(left, 0, w, h, p);

        } else {
            c.drawRect(left, 0, right, h, p);
        }
        return true;
    }

    private void drawPoint(Calendar calendar, int radius, Canvas c, Paint p) {
        if (calendar != null) {
            int w = c.getWidth();
            int h = c.getHeight();

            double minute = calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE);
            int x = (int) Math.round((minute / MINUTES_IN_DAY) * w);
            int y = h / 2;

            p.setStyle(Paint.Style.FILL);
            p.setColor(colorPointFill);
            c.drawCircle(x, y, radius, p);

            p.setStyle(Paint.Style.STROKE);
            p.setStrokeWidth(pointStrokeWidth);
            p.setColor(colorPointStroke);
            c.drawCircle(x, y, radius, p);
        }
    }

    /**
     * @param context a context used to access shared prefs
     */
    protected void loadSettings(Context context) {
        //Log.d("DEBUG", "LightMapView loadSettings (prefs)");
        if (isInEditMode()) {
            //noinspection UnnecessaryReturnStatement
            return;
        }
    }

    /**
     * @param context a context used to access resources
     * @param bundle a Bundle used to load state
     */
    protected void loadSettings(Context context, Bundle bundle) {
        //Log.d("DEBUG", "LightMapView loadSettings (bundle)");
    }

    /**
     * @param context a context used to access shared prefs
     * @return true settings were saved
     */
    protected boolean saveSettings(Context context) {
        //Log.d("DEBUG", "LightMap loadSettings (prefs)");
        return true;
    }

    /**
     * @param bundle a Bundle used to save state
     * @return true settings were saved
     */
    protected boolean saveSettings(Bundle bundle) {
        //Log.d("DEBUG", "LightMapView saveSettings (bundle)");
        return true;
    }

}