Package eu.hansolo.enzo.roundlcdclock

Source Code of eu.hansolo.enzo.roundlcdclock.RoundLcdClock

/*
* Copyright (c) 2013 by Gerrit Grunwald
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package eu.hansolo.enzo.roundlcdclock;


import eu.hansolo.enzo.fonts.Fonts;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.VPos;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;


/**
* User: hansolo
* Date: 27.12.13
* Time: 07:05
*/
public class RoundLcdClock extends Region {
    private static final double                PREFERRED_WIDTH  = 200;
    private static final double                PREFERRED_HEIGHT = 200;
    private static final double                MINIMUM_WIDTH    = 25;
    private static final double                MINIMUM_HEIGHT   = 25;
    private static final double                MAXIMUM_WIDTH    = 1024;
    private static final double                MAXIMUM_HEIGHT   = 1024;
    private volatile ScheduledFuture<?>        periodicClockTask;
    private static   ScheduledExecutorService  periodicClockExecutorService;
    private          double                    size;
    private          double                    width;
    private          double                    height;
    private          ObjectProperty<Color>     hColor;
    private          ObjectProperty<Color>     mColor;
    private          ObjectProperty<Color>     m5Color;
    private          ObjectProperty<Color>     sColor;
    private          ObjectProperty<Color>     timeColor;
    private          ObjectProperty<Color>     dateColor;
    private          BooleanProperty           alarmOn;
    private          ObjectProperty<LocalTime> alarm;
    private          BooleanProperty           dateVisible;
    private          BooleanProperty           alarmVisible;
    private          Pane                      pane;
    private          Canvas                    canvasBkg;
    private          GraphicsContext           ctxBkg;
    private          Canvas                    canvasFg;
    private          GraphicsContext           ctxFg;
    private          Canvas                    canvasHours;
    private          GraphicsContext           ctxHours;
    private          Canvas                    canvasMinutes;
    private          GraphicsContext           ctxMinutes;
    private          Canvas                    canvasSeconds;
    private          GraphicsContext           ctxSeconds;
    private          Font                      font;
    private          IntegerProperty           hours;
    private          IntegerProperty           minutes;
    private          IntegerProperty           seconds;
    private          boolean                   pm;
    private          StringBuilder             time;


    // ******************** Constructors **************************************
    public RoundLcdClock() {
        hColor       = new SimpleObjectProperty<>(this, "hourColor", Color.BLACK);
        mColor       = new SimpleObjectProperty<>(this, "minuteColor", Color.rgb(0, 0, 0, 0.5));
        m5Color      = new SimpleObjectProperty<>(this, "5MinuteColor", Color.BLACK);
        sColor       = new SimpleObjectProperty<>(this, "secondColor", Color.BLACK);
        timeColor    = new SimpleObjectProperty<>(this, "timeColor", Color.BLACK);
        dateColor    = new SimpleObjectProperty<>(this, "dateColor", Color.BLACK);
        alarmOn      = new SimpleBooleanProperty(this, "alarmOn", false);
        alarm        = new SimpleObjectProperty<>(LocalTime.now().minusMinutes(1));
        dateVisible  = new SimpleBooleanProperty(this, "dateVisible", false);
        alarmVisible = new SimpleBooleanProperty(this, "alarmVisible", false);
        time         = new StringBuilder();
        hours        = new SimpleIntegerProperty(this, "hours", -1);
        minutes      = new SimpleIntegerProperty(this, "minutes", -1);
        seconds      = new SimpleIntegerProperty(this, "seconds", -1);
        init();
        initGraphics();
        registerListeners();
        scheduleClockTask();
    }


    // ******************** Initialization ************************************
    private void init() {
        if (Double.compare(getPrefWidth(), 0.0) <= 0 || Double.compare(getPrefHeight(), 0.0) <= 0 ||
            Double.compare(getWidth(), 0.0) <= 0 || Double.compare(getHeight(), 0.0) <= 0) {
            if (getPrefWidth() > 0 && getPrefHeight() > 0) {
                setPrefSize(getPrefWidth(), getPrefHeight());
            } else {
                setPrefSize(PREFERRED_WIDTH, PREFERRED_HEIGHT);
            }
        }

        if (Double.compare(getMinWidth(), 0.0) <= 0 || Double.compare(getMinHeight(), 0.0) <= 0) {
            setMinSize(MINIMUM_WIDTH, MINIMUM_HEIGHT);
        }

        if (Double.compare(getMaxWidth(), 0.0) <= 0 || Double.compare(getMaxHeight(), 0.0) <= 0) {
            setMaxSize(MAXIMUM_WIDTH, MAXIMUM_HEIGHT);
        }
    }

    private void initGraphics() {
        canvasBkg     = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT);
        ctxBkg        = canvasBkg.getGraphicsContext2D();

        canvasFg      = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT);
        ctxFg         = canvasFg.getGraphicsContext2D();

        canvasHours   = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT);
        ctxHours      = canvasHours.getGraphicsContext2D();

        canvasMinutes = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT);
        ctxMinutes    = canvasMinutes.getGraphicsContext2D();

        canvasSeconds = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT);
        ctxSeconds    = canvasSeconds.getGraphicsContext2D();

        pane      = new Pane();
        pane.getChildren().setAll(canvasBkg, canvasFg, canvasHours, canvasMinutes, canvasSeconds);

        getChildren().setAll(pane);
    }

    private void registerListeners() {
        widthProperty().addListener(o -> handleControlPropertyChanged("RESIZE"));
        heightProperty().addListener(o -> handleControlPropertyChanged("RESIZE"));
        hColor.addListener(o -> handleControlPropertyChanged("REDRAW"));
        mColor.addListener(o -> handleControlPropertyChanged("REDRAW"));
        m5Color.addListener(o -> handleControlPropertyChanged("REDRAW"));
        sColor.addListener(o -> handleControlPropertyChanged("REDRAW"));
        timeColor.addListener(o -> handleControlPropertyChanged("REDRAW"));
        dateColor.addListener(o -> handleControlPropertyChanged("REDRAW"));
        alarmOn.addListener(o -> handleControlPropertyChanged("REDRAW"));
        dateVisible.addListener(o -> handleControlPropertyChanged("REDRAW"));
        alarmVisible.addListener(o -> handleControlPropertyChanged("REDRAW"));
        hours.addListener((ov, o, n) -> handleControlPropertyChanged("HOURS"));
        minutes.addListener((ov, o, n) -> handleControlPropertyChanged("MINUTES"));
        seconds.addListener((ov, o, n) -> handleControlPropertyChanged("SECONDS"));
    }


    // ******************** Methods *******************************************
    protected void handleControlPropertyChanged(final String PROPERTY) {
        if ("RESIZE".equals(PROPERTY)) {
            resize();
        } else if ("REDRAW".equals(PROPERTY)) {
            stopTask(periodicClockTask);
            drawBackground();
            drawForeground();
            scheduleClockTask();
        } else if ("SECONDS".equals(PROPERTY)) {
            drawForeground();
            drawSeconds();
        } else if ("MINUTES".equals(PROPERTY)) {
            drawMinutes();
        } else if ("HOURS".equals(PROPERTY)) {
            drawHours();
        }
    }

    public final Color getHourColor() {
        return hColor.get();
    }
    public final void setHourColor(final Color HOUR_COLOR) {
        hColor.set(HOUR_COLOR);
    }
    public final ObjectProperty<Color> hourColorProperty() {
        return hColor;
    }

    public final Color getMinuteColor() {
        return mColor.get();
    }
    public final void setMinuteColor(final Color MINUTE_COLOR) {
        mColor.set(MINUTE_COLOR);
    }
    public final ObjectProperty<Color> minuteColorProperty() {
        return mColor;
    }

    public final Color getMinute5Color() {
        return m5Color.get();
    }
    public final void setMinute5Color(final Color MINUTE_5_COLOR) {
        m5Color.set(MINUTE_5_COLOR);
    }
    public final ObjectProperty<Color> minute5ColorProperty() {
        return m5Color;
    }

    public final Color getSecondColor() {
        return sColor.get();
    }
    public final void setSecondColor(final Color SECOND_COLOR) {
        sColor.set(SECOND_COLOR);
    }
    public final ObjectProperty<Color> secondColorProperty() {
        return sColor;
    }

    public final Color getTimeColor() {
        return timeColor.get();
    }
    public final void setTimeColor(final Color TIME_COLOR) {
        timeColor.set(TIME_COLOR);
    }
    public final ObjectProperty<Color> timeColorProperty() {
        return timeColor;
    }

    public final Color getDateColor() {
        return dateColor.get();
    }
    public final void setDateColor(final Color DATE_COLOR) {
        dateColor.set(DATE_COLOR);
    }
    public final ObjectProperty<Color> dateColorProperty() {
        return dateColor;
    }

    public final boolean isAlarmOn() {
        return alarmOn.get();
    }
    public final void setAlarmOn(final boolean ALARM_ON) {
        alarmOn.set(ALARM_ON);
    }
    public final BooleanProperty alarmOnProperty() {
        return alarmOn;
    }

    public final LocalTime getAlarm() {
        return alarm.get();
    }
    public final void setAlarm(final LocalTime ALARM) {
        alarm.set(ALARM);
    }
    public final ObjectProperty<LocalTime> alarmProperty() {
        return alarm;
    }

    public final boolean isDateVisible() {
        return dateVisible.get();
    }
    public final void setDateVisible(final boolean DATE_VISIBLE) {
        dateVisible.set(DATE_VISIBLE);
    }
    public final BooleanProperty dateVisibleProperty() {
        return dateVisible;
    }

    public final boolean isAlarmVisible() {
        return alarmVisible.get();
    }
    public final void setAlarmVisible(final boolean ALARM_VISIBLE) {
        alarmVisible.set(ALARM_VISIBLE);
    }
    public final BooleanProperty alarmVisibleProperty() {
        return alarmVisible;
    }

    public final void setColor(final Color COLOR) {
        setHourColor(COLOR);
        setMinuteColor(Color.color(COLOR.getRed(), COLOR.getGreen(), COLOR.getBlue(), 0.6));
        setMinute5Color(COLOR);
        setSecondColor(COLOR);
        setTimeColor(COLOR);
        setDateColor(COLOR);
    }

    private void fireAlarmEvent() {
        fireEvent(new AlarmEvent(this, this, AlarmEvent.ALARM));
    }

    private void drawForeground() {
        ctxFg.clearRect(0, 0, size, size);

        // draw the time
        ctxFg.setFill(getTimeColor());
        font = Fonts.digital(0.2 * size);
        ctxFg.setTextBaseline(VPos.CENTER);
        ctxFg.setTextAlign(TextAlignment.CENTER);
        ctxFg.setFont(font);
        ctxFg.fillText(time.toString(), size * 0.5, size * 0.5);

        // draw the date
        if (isDateVisible()) {
            ctxFg.setFill(getDateColor());
            font = Fonts.digital(0.09 * size);
            ctxFg.setFont(font);
            ctxFg.fillText(LocalDate.now().format(DateTimeFormatter.ISO_DATE), size * 0.5, size * 0.65);
        }

        // draw the alarmOn icon
        if (isAlarmVisible() &&isAlarmOn()) drawAlarmIcon(ctxFg, ctxFg.getFill());
    }

    private void drawHours() {
        int hourCounter = 1;
        double strokeWidth = size * 0.06;
        ctxHours.setLineCap(StrokeLineCap.BUTT);
        ctxHours.clearRect(0, 0, size, size);
        for (int i = 450 ; i >= 90 ; i--) {
            ctxHours.save();
            if (i % 30 == 0) {
                //draw hours
                ctxHours.setStroke(getHourColor());
                ctxHours.setLineWidth(strokeWidth);
                if (hours.get() == 0 || hours.get() == 12) {
                    ctxHours.strokeArc(strokeWidth * 0.5, strokeWidth * 0.5, size - strokeWidth, size - strokeWidth, i + 1 - 30, 28, ArcType.OPEN);
                } else if (hourCounter <= (pm ? hours.get() - 12 : hours.get())) {
                    ctxHours.strokeArc(strokeWidth * 0.5, strokeWidth * 0.5, size - strokeWidth, size - strokeWidth, i + 1 - 30, 28, ArcType.OPEN);
                    hourCounter++;
                }
            }
            ctxFg.restore();
        }
    }

    private void drawMinutes() {
        int minCounter  = 1;
        double strokeWidth = size * 0.06;
        ctxMinutes.setLineCap(StrokeLineCap.BUTT);
        ctxMinutes.clearRect(0, 0, size, size);
        for (int i = 450 ; i >= 90 ; i--) {
            ctxMinutes.save();
            if (i % 6 == 0) {
                // draw minutes
                if (minCounter <= minutes.get()) {
                    ctxMinutes.setStroke(minCounter % 5 == 0 ? getMinute5Color() :  getMinuteColor());
                    ctxMinutes.setLineWidth(strokeWidth);
                    ctxMinutes.strokeArc(strokeWidth * 0.5 + strokeWidth * 1.1, strokeWidth * 0.5 + strokeWidth * 1.1, size - strokeWidth - strokeWidth * 2.2, size - strokeWidth - strokeWidth * 2.2, i + 1 - 6, 4, ArcType.OPEN);
                    minCounter++;
                }
            }
            ctxMinutes.restore();
        }
    }

    private void drawSeconds() {
        int secCounter  = 1;
        double strokeWidth = size * 0.06;
        ctxSeconds.setLineCap(StrokeLineCap.BUTT);
        ctxSeconds.clearRect(0, 0, size, size);
        for (int i = 450 ; i >= 90 ; i--) {
            ctxSeconds.save();
            if (i % 6 == 0) {
                // draw seconds
                if (secCounter <= seconds.get() + 1) {
                    ctxSeconds.setStroke(getSecondColor());
                    ctxSeconds.setLineWidth(strokeWidth * 0.25);
                    ctxSeconds.strokeArc(strokeWidth * 0.5 + strokeWidth * 1.8, strokeWidth * 0.5 + strokeWidth * 1.8, size - strokeWidth - strokeWidth * 3.6, size - strokeWidth - strokeWidth * 3.6, i + 1 - 6, 4, ArcType.OPEN);
                    secCounter++;
                }
            }
            ctxSeconds.restore();
        }
    }

    private void drawBackground() {
        double strokeWidth = size * 0.06;
        ctxBkg.setLineCap(StrokeLineCap.BUTT);
        ctxBkg.clearRect(0, 0, size, size);
        // draw translucent background
        ctxBkg.setStroke(Color.rgb(0, 12, 6, 0.1));
        IntStream.range(0, 360).forEach(
            i -> {
                ctxBkg.save();
                if (i % 6 == 0) {
                    // draw minutes
                    ctxBkg.setStroke(Color.color(mColor.get().getRed(), mColor.get().getGreen(), mColor.get().getBlue(), 0.1));
                    ctxBkg.setLineWidth(strokeWidth);
                    ctxBkg.strokeArc(strokeWidth * 0.5 + strokeWidth * 1.1, strokeWidth * 0.5 + strokeWidth * 1.1, size - strokeWidth - strokeWidth * 2.2, size - strokeWidth - strokeWidth * 2.2, i + 1, 4, ArcType.OPEN);

                    // draw seconds
                    ctxBkg.setStroke(Color.color(sColor.get().getRed(), sColor.get().getGreen(), sColor.get().getBlue(), 0.1));
                    ctxBkg.setLineWidth(strokeWidth * 0.25);
                    ctxBkg.strokeArc(strokeWidth * 0.5 + strokeWidth * 1.8, strokeWidth * 0.5 + strokeWidth * 1.8, size - strokeWidth - strokeWidth * 3.6, size - strokeWidth - strokeWidth * 3.6, i + 1, 4, ArcType.OPEN);
                }
                if (i % 30 == 0) {
                    //draw hours
                    ctxBkg.setStroke(Color.color(hColor.get().getRed(), hColor.get().getGreen(), hColor.get().getBlue(), 0.1));
                    ctxBkg.setLineWidth(strokeWidth);
                    ctxBkg.strokeArc(strokeWidth * 0.5, strokeWidth * 0.5, size - strokeWidth, size - strokeWidth, i + 1, 28, ArcType.OPEN);
                }
                ctxBkg.restore();   
            }
        );       

        // draw the time
        ctxBkg.setFill(Color.color(timeColor.get().getRed(), timeColor.get().getGreen(), timeColor.get().getBlue(), 0.1));
        font = Fonts.digital(0.2 * size);
        ctxBkg.setTextBaseline(VPos.CENTER);
        ctxBkg.setTextAlign(TextAlignment.CENTER);
        ctxBkg.setFont(font);
        ctxBkg.fillText("88:88", size * 0.5, size * 0.5);

        // draw the date
        if (isDateVisible()) {
            ctxBkg.setFill(Color.color(dateColor.get().getRed(), dateColor.get().getGreen(), dateColor.get().getBlue(), 0.1));
            font = Fonts.digital(0.09 * size);
            ctxBkg.setFont(font);
            ctxBkg.fillText("8888-88-88", size * 0.5, size * 0.65);
        }

        // draw the alarmOn icon
        if (isAlarmVisible() && !isAlarmOn()) drawAlarmIcon(ctxBkg, ctxBkg.getFill());
    }

    private void drawAlarmIcon(final GraphicsContext CTX, final Paint COLOR) {
        double iconSize = 0.1 * size;
        CTX.save();
        CTX.translate((size - iconSize) * 0.5, size * 0.25);
        CTX.beginPath();
        CTX.moveTo(0.6875 * iconSize, 0.875 * iconSize);
        CTX.bezierCurveTo(0.625 * iconSize, 0.9375 * iconSize, 0.5625 * iconSize, iconSize, 0.5 * iconSize, iconSize);
        CTX.bezierCurveTo(0.4375 * iconSize, iconSize, 0.375 * iconSize, 0.9375 * iconSize, 0.375 * iconSize, 0.875 * iconSize);
        CTX.bezierCurveTo(0.375 * iconSize, 0.875 * iconSize, 0.6875 * iconSize, 0.875 * iconSize, 0.6875 * iconSize, 0.875 * iconSize);
        CTX.closePath();
        CTX.moveTo(iconSize, 0.8125 * iconSize);
        CTX.bezierCurveTo(0.6875 * iconSize, 0.5625 * iconSize, 0.9375 * iconSize, 0.0, 0.5 * iconSize, 0.0);
        CTX.bezierCurveTo(0.5 * iconSize, 0.0, 0.5 * iconSize, 0.0, 0.5 * iconSize, 0.0);
        CTX.bezierCurveTo(0.5 * iconSize, 0.0, 0.5 * iconSize, 0.0, 0.5 * iconSize, 0.0);
        CTX.bezierCurveTo(0.125 * iconSize, 0.0, 0.375 * iconSize, 0.5625 * iconSize, 0.0, 0.8125 * iconSize);
        CTX.bezierCurveTo(0.0, 0.8125 * iconSize, 0.0, 0.8125 * iconSize, 0.0, 0.8125 * iconSize);
        CTX.bezierCurveTo(0.0, 0.8125 * iconSize, 0.0, 0.8125 * iconSize, 0.0, 0.8125 * iconSize);
        CTX.bezierCurveTo(0.0, 0.8125 * iconSize, 0.0, 0.8125 * iconSize, 0.0625 * iconSize, 0.8125 * iconSize);
        CTX.bezierCurveTo(0.0625 * iconSize, 0.8125 * iconSize, 0.5 * iconSize, 0.8125 * iconSize, 0.5 * iconSize, 0.8125 * iconSize);
        CTX.bezierCurveTo(0.5 * iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize);
        CTX.bezierCurveTo(iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize);
        CTX.bezierCurveTo(iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize);
        CTX.bezierCurveTo(iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize);
        CTX.closePath();
        CTX.setFill(COLOR);
        CTX.fill();
        CTX.restore();
    }

    private void updateClock() {
        time.setLength(0);

        LocalTime now = LocalTime.now();

        pm = now.get(ChronoField.AMPM_OF_DAY) == 1;
        hours.set(now.getHour());
        String hourString = Integer.toString(hours.get());
        if (hours.get() < 10) {
            time.append("0");
            time.append(hourString.substring(0, 1));
        } else {
            time.append(hourString.substring(0, 1));
            time.append(hourString.substring(1));
        }

        time.append(":");

        minutes.set(now.getMinute());
        String minutesString = Integer.toString(minutes.get());
        if (minutes.get() < 10) {
            time.append("0");
            time.append(minutesString.substring(0, 1));
        } else {
            time.append(minutesString.substring(0, 1));
            time.append(minutesString.substring(1));
        }

        seconds.set(now.getSecond());
       
        if (isAlarmOn() && now.isAfter(getAlarm()) && now.isBefore(getAlarm().plusNanos(105_000_000))) {
            fireAlarmEvent();
        }
    }


    // ******************** Scheduled tasks ***********************************
    private synchronized static void enableClockExecutorService() {
        if (null == periodicClockExecutorService) {
            periodicClockExecutorService = new ScheduledThreadPoolExecutor(1, getThreadFactory("RoundLcdClock", true));
        }
    }
    private synchronized void scheduleClockTask() {
        enableClockExecutorService();
        stopTask(periodicClockTask);
        periodicClockTask = periodicClockExecutorService.scheduleAtFixedRate(() -> {
            if (isVisible()) { Platform.runLater(() -> updateClock()); }
        }, 1, 1, TimeUnit.SECONDS);
    }
    private static ThreadFactory getThreadFactory(final String THREAD_NAME, final boolean IS_DAEMON) {
        return runnable -> {
            Thread thread = new Thread(runnable, THREAD_NAME);
            thread.setDaemon(IS_DAEMON);
            return thread;
        };
    }
    private void stopTask(ScheduledFuture<?> task) {
        if (null == task) return;

        task.cancel(true);
        task = null;
    }


    // ******************** Resizing ******************************************
    private void resize() {
        size   = getWidth() < getHeight() ? getWidth() : getHeight();
        width  = getWidth();
        height = getHeight();

        if (width > 0 && height > 0) {
            pane.setMaxSize(size, size);
            pane.relocate((getWidth() - size) * 0.5, (getHeight() - size) * 0.5);
           
            canvasBkg.setWidth(size);
            canvasBkg.setHeight(size);
            canvasFg.setWidth(size);
            canvasFg.setHeight(size);
            canvasHours.setWidth(size);
            canvasHours.setHeight(size);
            canvasMinutes.setWidth(size);
            canvasMinutes.setHeight(size);
            canvasSeconds.setWidth(size);
            canvasSeconds.setHeight(size);
            drawBackground();
            drawForeground();
            drawHours();
            drawMinutes();
            drawSeconds();
        }
    }
}
TOP

Related Classes of eu.hansolo.enzo.roundlcdclock.RoundLcdClock

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.