Allow user to change first day of the week

Closes #421
This commit is contained in:
2019-12-31 13:40:56 -06:00
parent 5cdb9eb9d5
commit 7801c933f0
31 changed files with 283 additions and 108 deletions

View File

@@ -412,7 +412,7 @@ public abstract class CheckmarkList
}
@NonNull
public List<Checkmark> groupBy(DateUtils.TruncateField field)
public List<Checkmark> groupBy(DateUtils.TruncateField field, int firstWeekday)
{
List<Checkmark> checks = getAll();
@@ -422,7 +422,7 @@ public abstract class CheckmarkList
for (Checkmark rep : checks)
{
Timestamp tt = rep.getTimestamp().truncate(field);
Timestamp tt = rep.getTimestamp().truncate(field, firstWeekday);
if (count == 0 || !truncatedTimestamps[count - 1].equals(tt))
truncatedTimestamps[count++] = tt;

View File

@@ -129,10 +129,10 @@ public abstract class ScoreList implements Iterable<Score>
return values;
}
public List<Score> groupBy(DateUtils.TruncateField field)
public List<Score> groupBy(DateUtils.TruncateField field, int firstWeekday)
{
computeAll();
HashMap<Timestamp, ArrayList<Double>> groups = getGroupedValues(field);
HashMap<Timestamp, ArrayList<Double>> groups = getGroupedValues(field, firstWeekday);
List<Score> scores = groupsToAvgScores(groups);
Collections.sort(scores, (s1, s2) -> s2.compareNewer(s1));
return scores;
@@ -293,14 +293,18 @@ public abstract class ScoreList implements Iterable<Score>
}
@NonNull
private HashMap<Timestamp, ArrayList<Double>> getGroupedValues(DateUtils.TruncateField field)
private HashMap<Timestamp, ArrayList<Double>> getGroupedValues(DateUtils.TruncateField field,
int firstWeekday)
{
HashMap<Timestamp, ArrayList<Double>> groups = new HashMap<>();
for (Score s : this)
{
Timestamp groupTimestamp = new Timestamp(
DateUtils.truncate(field, s.getTimestamp().getUnixTime()));
DateUtils.truncate(
field,
s.getTimestamp().getUnixTime(),
firstWeekday));
if (!groups.containsKey(groupTimestamp))
groups.put(groupTimestamp, new ArrayList<>());

View File

@@ -143,13 +143,17 @@ public final class Timestamp
return DateFormats.getCSVDateFormat().format(new Date(unixTime));
}
/**
* Returns an integer corresponding to the day of the week. Saturday maps
* to 0, Sunday maps to 1, and so on.
*/
public int getWeekday()
{
return toCalendar().get(DAY_OF_WEEK) % 7;
}
public Timestamp truncate(DateUtils.TruncateField field)
Timestamp truncate(DateUtils.TruncateField field, int firstWeekday)
{
return new Timestamp(DateUtils.truncate(field, unixTime));
return new Timestamp(DateUtils.truncate(field, unixTime, firstWeekday));
}
}

View File

@@ -333,6 +333,19 @@ public class Preferences
return Integer.parseInt(storage.getString("pref_widget_opacity", "102"));
}
/**
* @return An integer representing the first day of the week. Sunday
* corresponds to 1, Monday to 2, and so on, until Saturday, which is
* represented by 7. By default, this is based on the current system locale,
* unless the user changed this in the settings.
*/
public int getFirstWeekday()
{
String weekday = storage.getString("pref_first_weekday", "");
if (weekday.isEmpty()) return DateUtils.getFirstWeekdayNumberAccordingToLocale();
return Integer.parseInt(weekday);
}
public interface Listener
{
default void onCheckmarkSequenceChanged()

View File

@@ -22,6 +22,7 @@ package org.isoron.uhabits.core.utils;
import android.support.annotation.*;
import org.isoron.uhabits.core.models.*;
import org.jetbrains.annotations.*;
import java.util.*;
@@ -73,23 +74,6 @@ public abstract class DateUtils
return day;
}
private static String[] getDayNames(int format)
{
String[] wdays = new String[7];
Calendar day = new GregorianCalendar();
day.set(DAY_OF_WEEK, Calendar.SATURDAY);
for (int i = 0; i < wdays.length; i++)
{
wdays[i] =
day.getDisplayName(DAY_OF_WEEK, format, getLocale());
day.add(DAY_OF_MONTH, 1);
}
return wdays;
}
public static long getLocalTime()
{
if (fixedLocalTime != null) return fixedLocalTime;
@@ -100,19 +84,25 @@ public abstract class DateUtils
}
/**
* @return array with weekday names starting according to locale settings,
* e.g. [Mo,Di,Mi,Do,Fr,Sa,So] in Germany
* 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.
*/
public static String[] getLocaleDayNames(int format)
@NotNull
private static String[] getWeekdayNames(int format, int firstWeekday)
{
String[] days = new String[7];
Calendar calendar = new GregorianCalendar();
calendar.set(DAY_OF_WEEK, calendar.getFirstDayOfWeek());
for (int i = 0; i < days.length; i++)
{
calendar.set(DAY_OF_WEEK, firstWeekday);
for (int i = 0; i < days.length; i++) {
days[i] = calendar.getDisplayName(DAY_OF_WEEK, format,
getLocale());
getLocale());
calendar.add(DAY_OF_MONTH, 1);
}
@@ -120,30 +110,63 @@ public abstract class DateUtils
}
/**
* @return array with week days numbers starting according to locale
* settings, e.g. [2,3,4,5,6,7,1] in Europe
* 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 Integer[] getLocaleWeekdayList()
public static int[] getWeekdaySequence(int firstWeekday)
{
Integer[] dayNumbers = new Integer[7];
Calendar calendar = new GregorianCalendar();
calendar.set(DAY_OF_WEEK, calendar.getFirstDayOfWeek());
for (int i = 0; i < dayNumbers.length; i++)
return new int[]
{
dayNumbers[i] = calendar.get(DAY_OF_WEEK);
calendar.add(DAY_OF_MONTH, 1);
}
return dayNumbers;
(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,
};
}
public static String[] getLongDayNames()
/**
* @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 getDayNames(GregorianCalendar.LONG);
return new GregorianCalendar().getFirstDayOfWeek();
}
public static String[] getShortDayNames()
/**
* @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 getDayNames(SHORT);
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
@@ -206,10 +229,13 @@ public abstract class DateUtils
return Locale.getDefault();
}
public static Long truncate(TruncateField field, long timestamp)
public static Long truncate(TruncateField field,
long timestamp,
int firstWeekday)
{
GregorianCalendar cal = DateUtils.getCalendar(timestamp);
switch (field)
{
case MONTH:
@@ -217,7 +243,6 @@ public abstract class DateUtils
return cal.getTimeInMillis();
case WEEK_NUMBER:
int firstWeekday = cal.getFirstDayOfWeek();
int weekday = cal.get(DAY_OF_WEEK);
int delta = weekday - firstWeekday;
if (delta < 0) delta += 7;