From 358e1ccf4262fca74f9ce26162ebc70e9957b834 Mon Sep 17 00:00:00 2001 From: Marco Zanetti Date: Wed, 6 Jan 2021 03:33:49 +0100 Subject: [PATCH] Convert org.isoron.uhabits.core.utils to Kotlin (#699) --- .../common/views/FrequencyChart.java | 2 +- .../habits/list/views/HeaderView.kt | 7 +- .../{DateFormats.java => DateFormats.kt} | 40 +-- .../isoron/uhabits/core/utils/DateUtils.java | 333 ------------------ .../isoron/uhabits/core/utils/DateUtils.kt | 331 +++++++++++++++++ .../uhabits/core/utils/MidnightTimer.java | 77 ---- .../uhabits/core/utils/MidnightTimer.kt | 63 ++++ .../uhabits/core/utils/StringUtils.java | 67 ---- .../isoron/uhabits/core/utils/StringUtils.kt | 69 ++++ 9 files changed, 486 insertions(+), 503 deletions(-) rename android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/{DateFormats.java => DateFormats.kt} (53%) delete mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.kt delete mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/MidnightTimer.java create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/MidnightTimer.kt delete mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/StringUtils.java create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/StringUtils.kt diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.java index bfa91cd06..324119c73 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.java @@ -205,7 +205,7 @@ public class FrequencyChart extends ScrollableChart float rowHeight = rect.height() / 8.0f; prevRect.set(rect); - int[] localeWeekdayList = DateUtils.getWeekdaySequence(firstWeekday); + Integer[] localeWeekdayList = DateUtils.getWeekdaySequence(firstWeekday); for (int j = 0; j < localeWeekdayList.length; j++) { rect.set(0, 0, baseSize, baseSize); diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.kt index b4c8c131b..b349a8f2c 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.kt @@ -32,8 +32,7 @@ import android.view.View.MeasureSpec.EXACTLY import org.isoron.uhabits.R import org.isoron.uhabits.activities.common.views.ScrollableChart import org.isoron.uhabits.core.preferences.Preferences -import org.isoron.uhabits.core.utils.DateUtils.formatHeaderDate -import org.isoron.uhabits.core.utils.DateUtils.getStartOfTodayCalendarWithOffset +import org.isoron.uhabits.core.utils.DateUtils import org.isoron.uhabits.core.utils.MidnightTimer import org.isoron.uhabits.utils.dim import org.isoron.uhabits.utils.dp @@ -115,7 +114,7 @@ class HeaderView( } fun draw(canvas: Canvas) { - val day = getStartOfTodayCalendarWithOffset() + val day = DateUtils.getStartOfTodayCalendarWithOffset() val width = dim(R.dimen.checkmarkWidth) val height = dim(R.dimen.checkmarkHeight) val isReversed = prefs.isCheckmarkSequenceReversed @@ -139,7 +138,7 @@ class HeaderView( val y1 = rect.centerY() - 0.25 * em val y2 = rect.centerY() + 1.25 * em - val lines = formatHeaderDate(day).toUpperCase().split("\n") + val lines = DateUtils.formatHeaderDate(day).toUpperCase().split("\n") canvas.drawText(lines[0], rect.centerX(), y1.toFloat(), paint) canvas.drawText(lines[1], rect.centerX(), y2.toFloat(), paint) day.add(GregorianCalendar.DAY_OF_MONTH, -1) diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateFormats.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateFormats.kt similarity index 53% rename from android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateFormats.java rename to android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateFormats.kt index 42b164e20..9414f50a3 100644 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateFormats.java +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateFormats.kt @@ -17,31 +17,29 @@ * with this program. If not, see . */ -package org.isoron.uhabits.core.utils; +package org.isoron.uhabits.core.utils -import androidx.annotation.*; +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.TimeZone -import java.text.*; -import java.util.*; +class DateFormats { -public class DateFormats -{ - @NonNull - public static SimpleDateFormat fromSkeleton(@NonNull String skeleton, - @NonNull Locale locale) - { - SimpleDateFormat df = new SimpleDateFormat(skeleton, locale); - df.setTimeZone(TimeZone.getTimeZone("UTC")); - return df; - } + companion object { - public static SimpleDateFormat getBackupDateFormat() - { - return fromSkeleton("yyyy-MM-dd HHmmss", Locale.US); - } + @JvmStatic fun fromSkeleton( + skeleton: String, + locale: Locale + ): SimpleDateFormat { + val df = SimpleDateFormat(skeleton, locale) + df.timeZone = TimeZone.getTimeZone("UTC") + return df + } + + @JvmStatic fun getBackupDateFormat(): SimpleDateFormat = + fromSkeleton("yyyy-MM-dd HHmmss", Locale.US) - public static SimpleDateFormat getCSVDateFormat() - { - return fromSkeleton("yyyy-MM-dd", Locale.US); + @JvmStatic fun getCSVDateFormat(): SimpleDateFormat = + fromSkeleton("yyyy-MM-dd", Locale.US) } } diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java deleted file mode 100644 index aa3e89a54..000000000 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (C) 2016 Álinson Santos Xavier - * - * This file is part of Loop Habit Tracker. - * - * Loop Habit Tracker 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. - * - * Loop Habit Tracker 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 this program. If not, see . - */ - -package org.isoron.uhabits.core.utils; - -import androidx.annotation.*; - -import org.isoron.uhabits.core.models.*; -import org.jetbrains.annotations.*; - -import java.util.*; - -import static java.util.Calendar.*; - -public abstract class DateUtils -{ - - private static Long fixedLocalTime = null; - - private static TimeZone fixedTimeZone = null; - - private static Locale fixedLocale = null; - - private static int startDayHourOffset = 0; - - private static int startDayMinuteOffset = 0; - - /** - * Number of milliseconds in one minute. - */ - - public static final long MINUTE_LENGTH = 60 * 1000; - - /** - * Number of milliseconds in one hour. - */ - public static final long HOUR_LENGTH = 60 * MINUTE_LENGTH; - - /** - * Number of milliseconds in one day. - */ - public static final long DAY_LENGTH = 24 * HOUR_LENGTH; - - public static long applyTimezone(long localTimestamp) - { - TimeZone tz = getTimezone(); - return localTimestamp - tz.getOffset(localTimestamp - tz.getOffset(localTimestamp)); - } - - public static String formatHeaderDate(GregorianCalendar day) - { - Locale locale = getLocale(); - String dayOfMonth = Integer.toString(day.get(DAY_OF_MONTH)); - String dayOfWeek = day.getDisplayName(DAY_OF_WEEK, SHORT, locale); - return dayOfWeek + "\n" + dayOfMonth; - } - - private static GregorianCalendar getCalendar(long timestamp) - { - GregorianCalendar day = - new GregorianCalendar(TimeZone.getTimeZone("GMT"), getLocale()); - day.setTimeInMillis(timestamp); - return day; - } - - public static long getLocalTime() - { - if (fixedLocalTime != null) return fixedLocalTime; - - TimeZone tz = getTimezone(); - long now = new Date().getTime(); - return now + tz.getOffset(now); - } - - /** - * Returns an array of strings with the names for each day of the week, - * in either SHORT or LONG format. The first entry corresponds to the - * first day of the week, according to the provided argument. - * - * @param format Either GregorianCalendar.SHORT or LONG - * @param firstWeekday An integer representing the first day of the week, - * following java.util.Calendar conventions. That is, - * Saturday corresponds to 7, and Sunday corresponds - * to 1. - */ - @NotNull - private static String[] getWeekdayNames(int format, int firstWeekday) - { - String[] days = new String[7]; - Calendar calendar = new GregorianCalendar(); - calendar.set(DAY_OF_WEEK, firstWeekday); - for (int i = 0; i < days.length; i++) { - days[i] = calendar.getDisplayName(DAY_OF_WEEK, format, - getLocale()); - calendar.add(DAY_OF_MONTH, 1); - } - - return days; - } - - /** - * Returns a vector of exactly seven integers, where the first integer is - * the provided firstWeekday number, and each subsequent number is the - * previous number plus 1, wrapping back to 1 after 7. For example, - * providing 3 as firstWeekday returns {3,4,5,6,7,1,2} - * - * This function is supposed to be used to construct a sequence of weekday - * number following java.util.Calendar conventions. - */ - public static int[] getWeekdaySequence(int firstWeekday) - { - return new int[] - { - (firstWeekday - 1) % 7 + 1, - (firstWeekday) % 7 + 1, - (firstWeekday + 1) % 7 + 1, - (firstWeekday + 2) % 7 + 1, - (firstWeekday + 3) % 7 + 1, - (firstWeekday + 4) % 7 + 1, - (firstWeekday + 5) % 7 + 1, - }; - } - - /** - * @return An integer representing the first day of the week, according to - * the current locale. Sunday corresponds to 1, Monday to 2, and so on, - * until Saturday, which is represented by 7. This is consistent - * with java.util.Calendar constants. - */ - public static int getFirstWeekdayNumberAccordingToLocale() - { - return new GregorianCalendar().getFirstDayOfWeek(); - } - - /** - * @return A vector of strings with the long names for the week days, - * according to the current locale. The first entry corresponds to Saturday, - * the second entry corresponds to Monday, and so on. - * - * @param firstWeekday Either Calendar.SATURDAY, Calendar.MONDAY, or other - * weekdays defined in this class. - */ - public static String[] getLongWeekdayNames(int firstWeekday) - { - return getWeekdayNames(GregorianCalendar.LONG, firstWeekday); - } - - /** - * Returns a vector of strings with the short names for the week days, - * according to the current locale. The first entry corresponds to Saturday, - * the second entry corresponds to Monday, and so on. - * - * @param firstWeekday Either Calendar.SATURDAY, Calendar.MONDAY, or other - * weekdays defined in this class. - */ - public static String[] getShortWeekdayNames(int firstWeekday) - { - return getWeekdayNames(GregorianCalendar.SHORT, firstWeekday); - } - - @NonNull - public static Timestamp getToday() - { - return new Timestamp(getStartOfToday()); - } - - @NonNull - public static Timestamp getTodayWithOffset() - { - return new Timestamp(getStartOfTodayWithOffset()); - } - - public static long getStartOfDay(long timestamp) - { - return (timestamp / DAY_LENGTH) * DAY_LENGTH; - } - - public static long getStartOfDayWithOffset(long timestamp) - { - long offset = startDayHourOffset * HOUR_LENGTH + startDayMinuteOffset * MINUTE_LENGTH; - return getStartOfDay(timestamp - offset); - } - - public static long getStartOfToday() - { - return getStartOfDay(getLocalTime()); - } - - public static long getStartOfTomorrowWithOffset() - { - return getUpcomingTimeInMillis(startDayHourOffset, startDayMinuteOffset); - } - - public static long getStartOfTodayWithOffset() - { - return getStartOfDayWithOffset(getLocalTime()); - } - - public static long millisecondsUntilTomorrowWithOffset() - { - return getStartOfTomorrowWithOffset() - getLocalTime(); - } - - public static GregorianCalendar getStartOfTodayCalendar() - { - return getCalendar(getStartOfToday()); - } - - public static GregorianCalendar getStartOfTodayCalendarWithOffset() - { - return getCalendar(getStartOfTodayWithOffset()); - } - - private static TimeZone getTimezone() - { - if(fixedTimeZone != null) return fixedTimeZone; - return TimeZone.getDefault(); - } - - public static void setFixedTimeZone(TimeZone tz) - { - fixedTimeZone = tz; - } - - public static long removeTimezone(long timestamp) - { - TimeZone tz = getTimezone(); - return timestamp + tz.getOffset(timestamp); - } - - public static void setFixedLocalTime(Long timestamp) - { - fixedLocalTime = timestamp; - } - - public static void setFixedLocale(Locale locale) - { - fixedLocale = locale; - } - - public static void setStartDayOffset(int hourOffset, int minuteOffset) - { - startDayHourOffset = hourOffset; - startDayMinuteOffset = minuteOffset; - } - - private static Locale getLocale() - { - if(fixedLocale != null) return fixedLocale; - return Locale.getDefault(); - } - - public static Timestamp truncate(TruncateField field, - Timestamp timestamp, - int firstWeekday) - { - return new Timestamp(truncate(field, timestamp.getUnixTime(), firstWeekday)); - } - - public static Long truncate(TruncateField field, - long timestamp, - int firstWeekday) - { - GregorianCalendar cal = DateUtils.getCalendar(timestamp); - - - switch (field) - { - case DAY: - return cal.getTimeInMillis(); - - case MONTH: - cal.set(DAY_OF_MONTH, 1); - return cal.getTimeInMillis(); - - case WEEK_NUMBER: - int weekday = cal.get(DAY_OF_WEEK); - int delta = weekday - firstWeekday; - if (delta < 0) delta += 7; - cal.add(Calendar.DAY_OF_YEAR, -delta); - return cal.getTimeInMillis(); - - case QUARTER: - int quarter = cal.get(Calendar.MONTH) / 3; - cal.set(DAY_OF_MONTH, 1); - cal.set(Calendar.MONTH, quarter * 3); - return cal.getTimeInMillis(); - - case YEAR: - cal.set(Calendar.MONTH, Calendar.JANUARY); - cal.set(DAY_OF_MONTH, 1); - return cal.getTimeInMillis(); - - default: - throw new IllegalArgumentException(); - } - } - - public static long getUpcomingTimeInMillis(int hour, int minute) - { - Calendar calendar = DateUtils.getStartOfTodayCalendar(); - calendar.set(Calendar.HOUR_OF_DAY, hour); - calendar.set(Calendar.MINUTE, minute); - calendar.set(Calendar.SECOND, 0); - long time = calendar.getTimeInMillis(); - - if (DateUtils.getLocalTime() > time) - time += DateUtils.DAY_LENGTH; - - return applyTimezone(time); - } - - public enum TruncateField - { - DAY, MONTH, WEEK_NUMBER, YEAR, QUARTER - } -} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.kt new file mode 100644 index 000000000..0e3658389 --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/DateUtils.kt @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker 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. + * + * Loop Habit Tracker 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 this program. If not, see . + */ +package org.isoron.uhabits.core.utils + +import org.isoron.uhabits.core.models.Timestamp +import java.util.Calendar +import java.util.Calendar.DAY_OF_MONTH +import java.util.Calendar.DAY_OF_WEEK +import java.util.Calendar.SHORT +import java.util.Date +import java.util.GregorianCalendar +import java.util.Locale +import java.util.TimeZone + +abstract class DateUtils { + companion object { + private var fixedLocalTime: Long? = null + private var fixedTimeZone: TimeZone? = null + private var fixedLocale: Locale? = null + private var startDayHourOffset: Int = 0 + private var startDayMinuteOffset: Int = 0 + + /** + * Number of milliseconds in one minute. + */ + const val MINUTE_LENGTH: Long = 60 * 1000 + + /** + * Number of milliseconds in one hour. + */ + const val HOUR_LENGTH: Long = 60 * MINUTE_LENGTH + + /** + * Number of milliseconds in one day. + */ + const val DAY_LENGTH: Long = 24 * HOUR_LENGTH + + @JvmStatic + fun applyTimezone(localTimestamp: Long): Long { + val tz: TimeZone = getTimeZone() + return localTimestamp - tz.getOffset(localTimestamp - tz.getOffset(localTimestamp)) + } + + @JvmStatic + fun formatHeaderDate(day: GregorianCalendar): String { + val locale = getLocale() + val dayOfMonth: String = day.get(DAY_OF_MONTH).toString() + val dayOfWeek = day.getDisplayName(DAY_OF_WEEK, SHORT, locale) + return dayOfWeek + "\n" + dayOfMonth + } + + private fun getCalendar(timestamp: Long): GregorianCalendar { + val day = GregorianCalendar(TimeZone.getTimeZone("GMT"), getLocale()) + day.timeInMillis = timestamp + return day + } + + @JvmStatic + fun getLocalTime(): Long { + if (fixedLocalTime != null) return fixedLocalTime as Long + + val tz = getTimeZone() + val now = Date().getTime() + return now + tz.getOffset(now) + } + + /** + * Returns an array of strings with the names for each day of the week, + * in either SHORT or LONG format. The first entry corresponds to the + * first day of the week, according to the provided argument. + * + * @param format Either GregorianCalendar.SHORT or LONG + * @param firstWeekDay An integer representing the first day of the week, + * following java.util.Calendar conventions. That is, + * Saturday corresponds to 7, and Sunday corresponds + * to 1. + */ + private fun getWeekdayNames( + format: Int, + firstWeekDay: Int + ): Array { + val calendar = GregorianCalendar() + calendar.set(DAY_OF_WEEK, firstWeekDay) + + val daysNullable = ArrayList() + for (i in 1..7) { + daysNullable.add( + calendar.getDisplayName( + DAY_OF_WEEK, + format, + getLocale() + ) + ) + + calendar.add(DAY_OF_MONTH, 1) + } + + return daysNullable.toTypedArray() + } + + /** + * Returns a vector of exactly seven integers, where the first integer is + * the provided firstWeekday number, and each subsequent number is the + * previous number plus 1, wrapping back to 1 after 7. For example, + * providing 3 as firstWeekday returns {3,4,5,6,7,1,2} + * + * This function is supposed to be used to construct a sequence of weekday + * number following java.util.Calendar conventions. + */ + @JvmStatic + fun getWeekdaySequence(firstWeekday: Int): Array { + return arrayOf( + (firstWeekday - 1) % 7 + 1, + (firstWeekday) % 7 + 1, + (firstWeekday + 1) % 7 + 1, + (firstWeekday + 2) % 7 + 1, + (firstWeekday + 3) % 7 + 1, + (firstWeekday + 4) % 7 + 1, + (firstWeekday + 5) % 7 + 1, + ) + } + + /** + * @return An integer representing the first day of the week, according to + * the current locale. Sunday corresponds to 1, Monday to 2, and so on, + * until Saturday, which is represented by 7. This is consistent + * with java.util.Calendar constants. + */ + @JvmStatic + fun getFirstWeekdayNumberAccordingToLocale(): Int { + return GregorianCalendar().firstDayOfWeek + } + + /** + * @return A vector of strings with the long names for the week days, + * according to the current locale. The first entry corresponds to Saturday, + * the second entry corresponds to Monday, and so on. + * + * @param firstWeekday Either Calendar.SATURDAY, Calendar.MONDAY, or other + * weekdays defined in this class. + */ + @JvmStatic + fun getLongWeekdayNames(firstWeekday: Int): Array { + return getWeekdayNames(GregorianCalendar.LONG, firstWeekday) + } + + /** + * Returns a vector of strings with the short names for the week days, + * according to the current locale. The first entry corresponds to Saturday, + * the second entry corresponds to Monday, and so on. + * + * @param firstWeekday Either Calendar.SATURDAY, Calendar.MONDAY, or other + * weekdays defined in this class. + */ + @JvmStatic + fun getShortWeekdayNames(firstWeekday: Int): Array { + return getWeekdayNames(GregorianCalendar.SHORT, firstWeekday) + } + + @JvmStatic + fun getToday(): Timestamp = Timestamp(getStartOfToday()) + + @JvmStatic + fun getTodayWithOffset(): Timestamp = Timestamp(getStartOfTodayWithOffset()) + + @JvmStatic + fun getStartOfDay(timestamp: Long): Long = (timestamp / DAY_LENGTH) * DAY_LENGTH + + @JvmStatic + fun getStartOfDayWithOffset(timestamp: Long): Long { + val offset = startDayHourOffset * HOUR_LENGTH + startDayMinuteOffset * MINUTE_LENGTH + return getStartOfDay(timestamp - offset) + } + + @JvmStatic + fun getStartOfToday(): Long = getStartOfDay(getLocalTime()) + + @JvmStatic + fun getStartOfTomorrowWithOffset(): Long = getUpcomingTimeInMillis( + startDayHourOffset, + startDayMinuteOffset + ) + + @JvmStatic + fun getStartOfTodayWithOffset(): Long = getStartOfDayWithOffset(getLocalTime()) + + @JvmStatic + fun millisecondsUntilTomorrowWithOffset(): Long = getStartOfTomorrowWithOffset() - getLocalTime() + + @JvmStatic + fun getStartOfTodayCalendar(): GregorianCalendar = getCalendar(getStartOfToday()) + + @JvmStatic + fun getStartOfTodayCalendarWithOffset(): GregorianCalendar = getCalendar(getStartOfTodayWithOffset()) + + @JvmStatic + fun getTimeZone(): TimeZone { + return fixedTimeZone ?: TimeZone.getDefault() + } + + @JvmStatic + fun getTimezone(): TimeZone { + return fixedTimeZone ?: TimeZone.getDefault() + } + + @JvmStatic + fun removeTimezone(timestamp: Long): Long { + val tz = getTimeZone() + return timestamp + tz.getOffset(timestamp) + } + + @JvmStatic + fun setStartDayOffset(hourOffset: Int, minuteOffset: Int) { + startDayHourOffset = hourOffset + startDayMinuteOffset = minuteOffset + } + + @JvmStatic + fun getLocale(): Locale { + return fixedLocale ?: Locale.getDefault() + } + + @JvmStatic + fun truncate( + field: TruncateField, + timestamp: Timestamp, + firstWeekday: Int + ): Timestamp { + return Timestamp( + truncate( + field, + timestamp.unixTime, + firstWeekday + ) + ) + } + + @JvmStatic + fun truncate( + field: TruncateField, + timestamp: Long, + firstWeekday: Int + ): Long { + val cal = getCalendar(timestamp) + + return when (field) { + + TruncateField.DAY -> { cal.timeInMillis } + + TruncateField.MONTH -> { + cal.set(DAY_OF_MONTH, 1) + cal.timeInMillis + } + + TruncateField.WEEK_NUMBER -> { + val weekDay = cal.get(DAY_OF_WEEK) + var delta = weekDay - firstWeekday + if (delta < 0) { delta += 7 } + cal.add(Calendar.DAY_OF_YEAR, -delta) + cal.timeInMillis + } + + TruncateField.QUARTER -> { + val quarter = cal.get(Calendar.MONTH) / 3 + cal.set(DAY_OF_MONTH, 1) + cal.set(Calendar.MONTH, quarter * 3) + cal.timeInMillis + } + + TruncateField.YEAR -> { + cal.set(Calendar.MONTH, Calendar.JANUARY) + cal.set(DAY_OF_MONTH, 1) + cal.timeInMillis + } + } + } + + @JvmStatic + fun getUpcomingTimeInMillis( + hour: Int, + minute: Int + ): Long { + val calendar = getStartOfTodayCalendar() + calendar.set(Calendar.HOUR_OF_DAY, hour) + calendar.set(Calendar.MINUTE, minute) + calendar.set(Calendar.SECOND, 0) + var time = calendar.timeInMillis + + if (getLocalTime() > time) { + time += DAY_LENGTH + } + + return applyTimezone(time) + } + + @JvmStatic + fun setFixedLocalTime(newFixedLocalTime: Long?) { + this.fixedLocalTime = newFixedLocalTime + } + + @JvmStatic + fun setFixedTimeZone(newTimeZone: TimeZone?) { + this.fixedTimeZone = newTimeZone + } + + @JvmStatic + fun setFixedLocale(newLocale: Locale?) { + this.fixedLocale = newLocale + } + } + + enum class TruncateField { + DAY, MONTH, WEEK_NUMBER, YEAR, QUARTER + } +} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/MidnightTimer.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/MidnightTimer.java deleted file mode 100644 index 0beee5cd8..000000000 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/MidnightTimer.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2017 Álinson Santos Xavier - * - * This file is part of Loop Habit Tracker. - * - * Loop Habit Tracker 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. - * - * Loop Habit Tracker 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 this program. If not, see . - */ - -package org.isoron.uhabits.core.utils; - -import org.isoron.uhabits.core.*; - -import java.util.*; -import java.util.concurrent.*; - -import javax.inject.*; - -/** - * A class that emits events when a new day starts. - */ -@AppScope -public class MidnightTimer -{ - private final List listeners; - - private ScheduledExecutorService executor; - - @Inject - public MidnightTimer() - { - this.listeners = new LinkedList<>(); - } - - public synchronized void addListener(MidnightListener listener) - { - this.listeners.add(listener); - } - - public synchronized void onPause() - { - executor.shutdownNow(); - } - - public synchronized void onResume() - { - executor = Executors.newSingleThreadScheduledExecutor(); - executor.scheduleAtFixedRate(() -> notifyListeners(), - DateUtils.millisecondsUntilTomorrowWithOffset() + 1000, - DateUtils.DAY_LENGTH, TimeUnit.MILLISECONDS); - } - - public synchronized void removeListener(MidnightListener listener) - { - this.listeners.remove(listener); - } - - private synchronized void notifyListeners() - { - for (MidnightListener l : listeners) l.atMidnight(); - } - - public interface MidnightListener - { - void atMidnight(); - } -} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/MidnightTimer.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/MidnightTimer.kt new file mode 100644 index 000000000..ec322e55f --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/MidnightTimer.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2017 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker 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. + * + * Loop Habit Tracker 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 this program. If not, see . + */ +package org.isoron.uhabits.core.utils + +import org.isoron.uhabits.core.AppScope +import java.util.LinkedList +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.TimeUnit +import javax.inject.Inject + +/** + * A class that emits events when a new day starts. + */ +@AppScope +open class MidnightTimer @Inject constructor() { + private val listeners: MutableList = LinkedList() + private lateinit var executor: ScheduledExecutorService + + @Synchronized fun addListener(listener: MidnightListener) { + this.listeners.add(listener) + } + + @Synchronized fun onPause() = executor.shutdownNow() + + @Synchronized fun onResume() { + executor = Executors.newSingleThreadScheduledExecutor() + executor.scheduleAtFixedRate( + { notifyListeners() }, + DateUtils.millisecondsUntilTomorrowWithOffset() + 1000, + DateUtils.DAY_LENGTH, + TimeUnit.MILLISECONDS + ) + } + + @Synchronized fun removeListener(listener: MidnightListener) = this.listeners.remove(listener) + + @Synchronized private fun notifyListeners() { + for (l in listeners) { + l.atMidnight() + } + } + + interface MidnightListener { + fun atMidnight() + } +} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/StringUtils.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/StringUtils.java deleted file mode 100644 index a31b261ee..000000000 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/StringUtils.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2017 Álinson Santos Xavier - * - * This file is part of Loop Habit Tracker. - * - * Loop Habit Tracker 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. - * - * Loop Habit Tracker 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 this program. If not, see . - */ - -package org.isoron.uhabits.core.utils; - -import org.apache.commons.lang3.builder.*; - -import java.math.*; -import java.util.*; - -public class StringUtils -{ - private static StandardToStringStyle toStringStyle = null; - - public static String getRandomId() - { - return new BigInteger(260, new Random()).toString(32).substring(0, 32); - } - - public static ToStringStyle defaultToStringStyle() - { - if (toStringStyle == null) - { - toStringStyle = new StandardToStringStyle(); - toStringStyle.setFieldSeparator(", "); - toStringStyle.setUseClassName(false); - toStringStyle.setUseIdentityHashCode(false); - toStringStyle.setContentStart("{"); - toStringStyle.setContentEnd("}"); - toStringStyle.setFieldNameValueSeparator(": "); - toStringStyle.setArrayStart("["); - toStringStyle.setArrayEnd("]"); - } - - return toStringStyle; - } - - public static String joinLongs(long values[]) - { - return org.apache.commons.lang3.StringUtils.join(values, ','); - } - - public static long[] splitLongs(String str) - { - String parts[] = org.apache.commons.lang3.StringUtils.split(str, ','); - - long numbers[] = new long[parts.length]; - for (int i = 0; i < parts.length; i++) numbers[i] = Long.valueOf(parts[i]); - return numbers; - } -} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/StringUtils.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/StringUtils.kt new file mode 100644 index 000000000..5ad68cad9 --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/utils/StringUtils.kt @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker 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. + * + * Loop Habit Tracker 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 this program. If not, see . + */ + +package org.isoron.uhabits.core.utils + +import org.apache.commons.lang3.builder.StandardToStringStyle +import org.apache.commons.lang3.builder.ToStringStyle +import java.math.BigInteger +import java.util.Random + +class StringUtils { + + companion object { + private lateinit var toStringStyle: StandardToStringStyle + + @JvmStatic + fun getRandomId(): String { + return BigInteger(260, Random()).toString(32).subSequence(0, 32).toString() + } + + @JvmStatic + fun defaultToStringStyle(): ToStringStyle { + toStringStyle = StandardToStringStyle() + toStringStyle.apply { + fieldSeparator = ", " + isUseClassName = false + isUseIdentityHashCode = false + contentStart = "{" + contentEnd = "}" + fieldNameValueSeparator = ": " + arrayStart = "[" + arrayEnd = "]" + } + + return toStringStyle + } + + @JvmStatic + fun joinLongs(values: LongArray): String { + return org.apache.commons.lang3.StringUtils.join(values, ',') + } + + @JvmStatic + fun splitLongs(str: String): LongArray { + val parts: Array = org.apache.commons.lang3.StringUtils.split(str, ',') + val numbers = LongArray(parts.size) { + i -> + parts[i].toLong() + } + return numbers + } + } +}