// -*- mode: java; c-basic-offset: 2; -*- // Copyright 2009-2011 Google, All Rights reserved // Copyright 2011-2012 MIT, All rights reserved // Released under the Apache License, Version 2.0 // http://www.apache.org/licenses/LICENSE-2.0 package com.google.appinventor.components.runtime; import com.google.appinventor.components.annotations.DesignerComponent; import com.google.appinventor.components.annotations.DesignerProperty; import com.google.appinventor.components.annotations.PropertyCategory; import com.google.appinventor.components.annotations.SimpleEvent; import com.google.appinventor.components.annotations.SimpleFunction; import com.google.appinventor.components.annotations.SimpleObject; import com.google.appinventor.components.annotations.SimpleProperty; import com.google.appinventor.components.common.ComponentCategory; import com.google.appinventor.components.common.PropertyTypeConstants; import com.google.appinventor.components.common.YaVersion; import com.google.appinventor.components.runtime.errors.YailRuntimeError; import com.google.appinventor.components.runtime.util.Dates; import com.google.appinventor.components.runtime.util.TimerInternal; import java.util.Calendar; /** * Clock provides the phone's clock, a timer, calendar and time calculations. * Everything is represented in milliseconds. * */ @DesignerComponent(version = YaVersion.CLOCK_COMPONENT_VERSION, description = "<p>Non-visible component that provides the instant in time using the internal clock on th" + "e phone. It can fire a timer at regularly set intervals and perform time calculations, manipulations, and conversions.</p> " + "<p>Methods to convert an instant to text are also available. Acceptable patterns are empty string, MM/DD/YYYY HH:mm:ss a, or MMM d, yyyy" + "HH:mm. The empty string will provide the default format, which is \"MMM d, yyyy HH:mm:ss a\" for FormatDateTime \"MMM d, yyyy\" for FormatDate. " + "To see all possible format, please see <a href=\"https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html\" _target=\"_blank\">here</a>. </p> ", category = ComponentCategory.SENSORS, nonVisible = true, iconName = "images/clock.png") @SimpleObject public final class Clock extends AndroidNonvisibleComponent implements Component, AlarmHandler, OnStopListener, OnResumeListener, OnDestroyListener, Deleteable { private static final int DEFAULT_INTERVAL = 1000; // ms private static final boolean DEFAULT_ENABLED = true; private TimerInternal timerInternal; private boolean timerAlwaysFires = true; private boolean onScreen = false; /** * Creates a new Clock component. * * @param container ignored (because this is a non-visible component) */ public Clock(ComponentContainer container) { super(container.$form()); timerInternal = new TimerInternal(this, DEFAULT_ENABLED, DEFAULT_INTERVAL); // Set up listeners form.registerForOnResume(this); form.registerForOnStop(this); form.registerForOnDestroy(this); if (form instanceof ReplForm) { // In REPL, if this Clock component was added to the project after the onResume call occurred, // then onScreen would be false, but the REPL app is, in fact, on screen. onScreen = true; } } // Only the above constructor should be used in practice. public Clock() { super(null); // To allow testing without Timer } /** * Default Timer event handler. */ @SimpleEvent( description = "Timer has gone off.") public void Timer() { if (timerAlwaysFires || onScreen) { EventDispatcher.dispatchEvent(this, "Timer"); } } /** * Interval property getter method. * * @return timer interval in ms */ @SimpleProperty( category = PropertyCategory.BEHAVIOR, description ="Interval between timer events in ms") public int TimerInterval() { return timerInternal.Interval(); } /** * Interval property setter method: sets the interval between timer events. * * @param interval timer interval in ms */ @DesignerProperty( editorType = PropertyTypeConstants.PROPERTY_TYPE_NON_NEGATIVE_INTEGER, defaultValue = DEFAULT_INTERVAL + "") @SimpleProperty public void TimerInterval(int interval) { timerInternal.Interval(interval); } /** * Enabled property getter method. * * @return {@code true} indicates a running timer, {@code false} a stopped * timer */ @SimpleProperty( category = PropertyCategory.BEHAVIOR, description = "Fires timer if true") public boolean TimerEnabled() { return timerInternal.Enabled(); } /** * Enabled property setter method: starts or stops the timer. * * @param enabled {@code true} starts the timer, {@code false} stops it */ @DesignerProperty( editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = DEFAULT_ENABLED ? "True" : "False") @SimpleProperty public void TimerEnabled(boolean enabled) { timerInternal.Enabled(enabled); } /** * TimerAlwaysFires property getter method. * * return {@code true} if the timer event will fire even if the application * is not on the screen */ @SimpleProperty( category = PropertyCategory.BEHAVIOR, description = "Will fire even when application is not showing on the " + "screen if true") public boolean TimerAlwaysFires() { return timerAlwaysFires; } /** * TimerAlwaysFires property setter method: instructs when to disable * * @param always {@code true} if the timer event should fire even if the * application is not on the screen */ @DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "True") @SimpleProperty public void TimerAlwaysFires(boolean always) { timerAlwaysFires = always; } // AlarmHandler implementation @Override public void alarm() { Timer(); } /** * Returns the current system time in milliseconds. * * @return current system time in milliseconds */ @SimpleFunction (description = "The phone's internal time") public static long SystemTime() { return Dates.Timer(); } @SimpleFunction(description = "The current instant in time read from " + "phone's clock") public static Calendar Now() { return Dates.Now(); } /** * An instant in time specified by MM/DD/YYYY hh:mm:ss or MM/DD/YYYY or hh:mm * where MM is the month (01-12), DD the day (01-31), YYYY the year * (0000-9999), hh the hours (00-23), mm the minutes (00-59) and ss * the seconds (00-59). * * @param from string to convert * @return date */ @SimpleFunction( description = "An instant in time specified by MM/DD/YYYY hh:mm:ss or MM/DD/YYYY or hh:mm") public static Calendar MakeInstant(String from) { try { return Dates.DateValue(from); } catch (IllegalArgumentException e) { throw new YailRuntimeError( "Argument to MakeInstant should have form MM/DD/YYYY hh:mm:ss, or MM/DD/YYYY or hh:mm", "Sorry to be so picky."); } } /** * Create an Calendar from ms since 1/1/1970 00:00:00.0000 * Probably should go in Calendar. * * @param millis raw millisecond number. */ @SimpleFunction(description = "An instant in time specified by the milliseconds since 1970.") public static Calendar MakeInstantFromMillis(long millis) { Calendar instant = Dates.Now(); // just to get our hands on an instant instant.setTimeInMillis(millis); return instant; } /** * Calendar property getter method: gets the raw millisecond representation of * a Calendar. * @param instant Calendar * @return milliseconds since 1/1/1970. */ @SimpleFunction (description = "The instant in time measured as milliseconds since 1970.") public static long GetMillis(Calendar instant) { return instant.getTimeInMillis(); } @SimpleFunction(description = "An instant in time some duration after the argument") public static Calendar AddDuration(Calendar instant, long quantity) { Calendar newInstant = (Calendar) instant.clone(); Dates.DateAddInMillis(newInstant, quantity); return newInstant; } @SimpleFunction(description = "An instant in time some seconds after the argument") public static Calendar AddSeconds(Calendar instant, int quantity) { Calendar newInstant = (Calendar) instant.clone(); Dates.DateAdd(newInstant, Calendar.SECOND, quantity); return newInstant; } @SimpleFunction(description = "An instant in time some minutes after the argument") public static Calendar AddMinutes(Calendar instant, int quantity) { Calendar newInstant = (Calendar) instant.clone(); Dates.DateAdd(newInstant, Calendar.MINUTE, quantity); return newInstant; } @SimpleFunction(description = "An instant in time some hours after the argument") public static Calendar AddHours(Calendar instant, int quantity) { Calendar newInstant = (Calendar) instant.clone(); Dates.DateAdd(newInstant, Calendar.HOUR_OF_DAY, quantity); return newInstant; } @SimpleFunction(description = "An instant in time some days after the argument") public static Calendar AddDays(Calendar instant, int quantity) { Calendar newInstant = (Calendar) instant.clone(); Dates.DateAdd(newInstant, Calendar.DATE, quantity); return newInstant; } @SimpleFunction(description = "An instant in time some weeks after the argument") public static Calendar AddWeeks(Calendar instant, int quantity) { Calendar newInstant = (Calendar) instant.clone(); Dates.DateAdd(newInstant, Calendar.WEEK_OF_YEAR, quantity); return newInstant; } @SimpleFunction(description = "An instant in time some months after the argument") public static Calendar AddMonths(Calendar instant, int quantity) { Calendar newInstant = (Calendar) instant.clone(); Dates.DateAdd(newInstant, Calendar.MONTH, quantity); return newInstant; } @SimpleFunction(description = "An instant in time some years after the argument") public static Calendar AddYears(Calendar instant, int quantity) { Calendar newInstant = (Calendar) instant.clone(); Dates.DateAdd(newInstant, Calendar.YEAR, quantity); return newInstant; } /** * Returns the milliseconds by which end follows start (+ or -) * * @param start beginning instant * @param end ending instant * @return milliseconds */ @SimpleFunction (description = "Milliseconds elapsed between instants") public static long Duration(Calendar start, Calendar end) { return end.getTimeInMillis() - start.getTimeInMillis(); } /** * Returns the duration converted from milliseconds to seconds. * * @param duration time interval to convert * @return duration in seconds */ @SimpleFunction (description = "convert duration to seconds") public static long DurationToSeconds(long duration) { return Dates.ConvertDuration(duration, Calendar.SECOND); } /** * Returns the duration converted from milliseconds to minutes. * * @param duration time interval to convert * @return duration in minutes */ @SimpleFunction (description = "convert duration to minutes") public static long DurationToMinutes(long duration) { return Dates.ConvertDuration(duration, Calendar.MINUTE); } /** * Returns the duration converted from milliseconds to hours. * * @param duration time interval to convert * @return duration in hours */ @SimpleFunction (description = "convert duration to hours") public static long DurationToHours(long duration) { return Dates.ConvertDuration(duration, Calendar.HOUR_OF_DAY); } /** * Returns the duration converted from milliseconds to days. * * @param duration time interval to convert * @return duration in days */ @SimpleFunction (description = "convert duration to days") public static long DurationToDays(long duration) { return Dates.ConvertDuration(duration, Calendar.DATE); } /** * Returns the duration converted from milliseconds to weeks. * * @param duration time interval to convert * @return duration in weeks */ @SimpleFunction (description = "convert duration to weeks") public static long DurationToWeeks(long duration) { return Dates.ConvertDuration(duration, Calendar.WEEK_OF_YEAR); } /** * Returns the seconds for the given instant. * * @param instant instant to use seconds of * @return seconds (range 0 - 59) */ @SimpleFunction (description = "The second of the minute") public static int Second(Calendar instant) { return Dates.Second(instant); } /** * Returns the minutes for the given date. * * @param instant instant to use minutes of * @return minutes (range 0 - 59) */ @SimpleFunction(description = "The minute of the hour") public static int Minute(Calendar instant) { return Dates.Minute(instant); } /** * Returns the hours for the given date. * * @param instant Calendar to use hours of * @return hours (range 0 - 23) */ @SimpleFunction (description = "The hour of the day") public static int Hour(Calendar instant) { return Dates.Hour(instant); } /** * Returns the day of the month. * * @param instant instant to use day of the month of * @return day: [1...31] */ @SimpleFunction (description = "The day of the month") public static int DayOfMonth(Calendar instant) { return Dates.Day(instant); } /** * Returns the weekday for the given instant. * * @param instant instant to use day of week of * @return day of week: [1...7] starting with Sunday */ @SimpleFunction (description = "The day of the week represented as a " + "number from 1 (Sunday) to 7 (Saturday)") public static int Weekday(Calendar instant) { return Dates.Weekday(instant); } /** * Returns the name of the weekday for the given instant. * * @param instant instant to use weekday of * @return weekday, as a string. */ @SimpleFunction (description = "The name of the day of the week") public static String WeekdayName(Calendar instant) { return Dates.WeekdayName(instant); } /** * Returns the number of the month for the given instant. * * @param instant instant to use month of * @return number of month */ @SimpleFunction (description = "The month of the year represented as a " + "number from 1 to 12)") public static int Month(Calendar instant) { return Dates.Month(instant) + 1; } /** * Returns the name of the month for the given instant. * * @param instant instant to use month of * @return name of month */ @SimpleFunction (description = "The name of the month") public static String MonthName(Calendar instant) { return Dates.MonthName(instant); } /** * Returns the year of the given instant. * * @param instant instant to use year of * @return year */ @SimpleFunction(description = "The year") public static int Year(Calendar instant) { return Dates.Year(instant); } /** * Converts and formats an instant into a string of date and time with the specified pattern. * * * @param instant instant to format * @param pattern format of the date and time e.g. MM/DD/YYYY HH:mm:ss a, MMM d, yyyy HH:mm * @return formatted instant */ @SimpleFunction (description = "Text representing the date and time of an" + " instant in the specified pattern") public static String FormatDateTime(Calendar instant, String pattern) { try { return Dates.FormatDateTime(instant, pattern); } catch (IllegalArgumentException e){ throw new YailRuntimeError( "Illegal argument for pattern in Clock.FormatDateTime. Acceptable values are empty string, MM/DD/YYYY HH:mm:ss a, MMM d, yyyy HH:mm " + "For all possible patterns, see https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html", "Sorry to be so picky."); } } /** * Converts and formats an instant into a string of date with the specified pattern. * * @param instant instant to format * @param pattern format of the date e.g. MM/DD/YYYY or MMM d, yyyy * @return formatted instant */ @SimpleFunction (description = "Text representing the date of an instant in the specified pattern") public static String FormatDate(Calendar instant, String pattern) { try { return Dates.FormatDate(instant, pattern); } catch (IllegalArgumentException e){ throw new YailRuntimeError( "Illegal argument for pattern in Clock.FormatDate. Acceptable values are empty string, MM/dd/YYYY, or MMM d, yyyy. " + "For all possible patterns, see https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html" ,"Sorry to be so picky."); } } /** * Converts and formats the given instant into a string. * * @param instant instant to format * @return formatted instant */ @SimpleFunction (description = "Text representing the time of an instant") public static String FormatTime(Calendar instant) { return Dates.FormatTime(instant); } @Override public void onStop() { onScreen = false; } @Override public void onResume() { onScreen = true; } @Override public void onDestroy() { timerInternal.Enabled(false); } @Override public void onDelete() { timerInternal.Enabled(false); } }