mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
@@ -6,6 +6,7 @@
|
|||||||
* Improved calculation of streaks for non-daily habits: performing habits on irregular weekdays will no longer break your streak.
|
* Improved calculation of streaks for non-daily habits: performing habits on irregular weekdays will no longer break your streak.
|
||||||
* Many more colors to choose from (now 20 in total).
|
* Many more colors to choose from (now 20 in total).
|
||||||
* Ability to customize how transparent the widgets are on your home screen.
|
* Ability to customize how transparent the widgets are on your home screen.
|
||||||
|
* Ability to customize the first day of the week.
|
||||||
* Yes/No buttons on notifications, instead of just "Check".
|
* Yes/No buttons on notifications, instead of just "Check".
|
||||||
* Automatic dark theme according to phone settings (Android 10).
|
* Automatic dark theme according to phone settings (Android 10).
|
||||||
* 25% smaller APK size and much smaller backup files.
|
* 25% smaller APK size and much smaller backup files.
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import org.isoron.uhabits.utils.*;
|
|||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
import org.junit.runner.*;
|
import org.junit.runner.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public class ScoreChartTest extends BaseViewTest
|
public class ScoreChartTest extends BaseViewTest
|
||||||
@@ -80,7 +82,7 @@ public class ScoreChartTest extends BaseViewTest
|
|||||||
@Test
|
@Test
|
||||||
public void testRender_withMonthlyBucket() throws Throwable
|
public void testRender_withMonthlyBucket() throws Throwable
|
||||||
{
|
{
|
||||||
view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.MONTH));
|
view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.MONTH, Calendar.SUNDAY));
|
||||||
view.setBucketSize(30);
|
view.setBucketSize(30);
|
||||||
view.invalidate();
|
view.invalidate();
|
||||||
|
|
||||||
@@ -97,7 +99,7 @@ public class ScoreChartTest extends BaseViewTest
|
|||||||
@Test
|
@Test
|
||||||
public void testRender_withYearlyBucket() throws Throwable
|
public void testRender_withYearlyBucket() throws Throwable
|
||||||
{
|
{
|
||||||
view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.YEAR));
|
view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.YEAR, Calendar.SUNDAY));
|
||||||
view.setBucketSize(365);
|
view.setBucketSize(365);
|
||||||
view.invalidate();
|
view.invalidate();
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import org.isoron.uhabits.core.models.*;
|
|||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
import org.junit.runner.*;
|
import org.junit.runner.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public class FrequencyWidgetTest extends BaseViewTest
|
public class FrequencyWidgetTest extends BaseViewTest
|
||||||
@@ -45,7 +47,7 @@ public class FrequencyWidgetTest extends BaseViewTest
|
|||||||
setTheme(R.style.WidgetTheme);
|
setTheme(R.style.WidgetTheme);
|
||||||
|
|
||||||
habit = fixtures.createLongHabit();
|
habit = fixtures.createLongHabit();
|
||||||
FrequencyWidget widget = new FrequencyWidget(targetContext, 0, habit);
|
FrequencyWidget widget = new FrequencyWidget(targetContext, 0, habit, Calendar.SUNDAY);
|
||||||
view = convertToView(widget, 400, 400);
|
view = convertToView(widget, 400, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import org.isoron.uhabits.core.models.*;
|
|||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
import org.junit.runner.*;
|
import org.junit.runner.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public class HistoryWidgetTest extends BaseViewTest
|
public class HistoryWidgetTest extends BaseViewTest
|
||||||
@@ -45,7 +47,7 @@ public class HistoryWidgetTest extends BaseViewTest
|
|||||||
setTheme(R.style.WidgetTheme);
|
setTheme(R.style.WidgetTheme);
|
||||||
|
|
||||||
habit = fixtures.createLongHabit();
|
habit = fixtures.createLongHabit();
|
||||||
HistoryWidget widget = new HistoryWidget(targetContext, 0, habit);
|
HistoryWidget widget = new HistoryWidget(targetContext, 0, habit, Calendar.SUNDAY);
|
||||||
view = convertToView(widget, 400, 400);
|
view = convertToView(widget, 400, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import android.util.*;
|
|||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.activities.common.views.*;
|
import org.isoron.uhabits.activities.common.views.*;
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
|
import org.isoron.uhabits.core.preferences.*;
|
||||||
import org.isoron.uhabits.core.tasks.*;
|
import org.isoron.uhabits.core.tasks.*;
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
@@ -51,6 +52,8 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
|
|||||||
|
|
||||||
private TaskRunner taskRunner;
|
private TaskRunner taskRunner;
|
||||||
|
|
||||||
|
private Preferences prefs;
|
||||||
|
|
||||||
public HistoryEditorDialog()
|
public HistoryEditorDialog()
|
||||||
{
|
{
|
||||||
this.controller = new Controller() {};
|
this.controller = new Controller() {};
|
||||||
@@ -72,9 +75,11 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
|
|||||||
(HabitsApplication) getActivity().getApplicationContext();
|
(HabitsApplication) getActivity().getApplicationContext();
|
||||||
habitList = app.getComponent().getHabitList();
|
habitList = app.getComponent().getHabitList();
|
||||||
taskRunner = app.getComponent().getTaskRunner();
|
taskRunner = app.getComponent().getTaskRunner();
|
||||||
|
prefs = app.getComponent().getPreferences();
|
||||||
|
|
||||||
historyChart = new HistoryChart(context);
|
historyChart = new HistoryChart(context);
|
||||||
historyChart.setController(controller);
|
historyChart.setController(controller);
|
||||||
|
historyChart.setFirstWeekday(prefs.getFirstWeekday());
|
||||||
|
|
||||||
if (savedInstanceState != null)
|
if (savedInstanceState != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import org.isoron.uhabits.*;
|
|||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
import org.isoron.uhabits.core.utils.*;
|
import org.isoron.uhabits.core.utils.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog that allows the user to pick one or more days of the week.
|
* Dialog that allows the user to pick one or more days of the week.
|
||||||
*/
|
*/
|
||||||
@@ -59,8 +61,9 @@ public class WeekdayPickerDialog extends AppCompatDialogFragment implements
|
|||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
builder
|
builder
|
||||||
.setTitle(R.string.select_weekdays)
|
.setTitle(R.string.select_weekdays)
|
||||||
.setMultiChoiceItems(DateUtils.getLongDayNames(), selectedDays,
|
.setMultiChoiceItems(DateUtils.getLongWeekdayNames(Calendar.SATURDAY),
|
||||||
this)
|
selectedDays,
|
||||||
|
this)
|
||||||
.setPositiveButton(android.R.string.yes, this)
|
.setPositiveButton(android.R.string.yes, this)
|
||||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
|
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
|
||||||
dismiss();
|
dismiss();
|
||||||
|
|||||||
@@ -69,8 +69,11 @@ public class FrequencyChart extends ScrollableChart
|
|||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private HashMap<Timestamp, Integer[]> frequency;
|
private HashMap<Timestamp, Integer[]> frequency;
|
||||||
|
|
||||||
private int maxFreq;
|
private int maxFreq;
|
||||||
|
|
||||||
|
private int firstWeekday = Calendar.SUNDAY;
|
||||||
|
|
||||||
public FrequencyChart(Context context)
|
public FrequencyChart(Context context)
|
||||||
{
|
{
|
||||||
super(context);
|
super(context);
|
||||||
@@ -98,6 +101,12 @@ public class FrequencyChart extends ScrollableChart
|
|||||||
postInvalidate();
|
postInvalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFirstWeekday(int firstWeekday)
|
||||||
|
{
|
||||||
|
this.firstWeekday = firstWeekday;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
private int getMaxFreq(HashMap<Timestamp, Integer[]> frequency)
|
private int getMaxFreq(HashMap<Timestamp, Integer[]> frequency)
|
||||||
{
|
{
|
||||||
int maxValue = 1;
|
int maxValue = 1;
|
||||||
@@ -144,7 +153,6 @@ public class FrequencyChart extends ScrollableChart
|
|||||||
prevRect.setEmpty();
|
prevRect.setEmpty();
|
||||||
|
|
||||||
GregorianCalendar currentDate = DateUtils.getStartOfTodayCalendar();
|
GregorianCalendar currentDate = DateUtils.getStartOfTodayCalendar();
|
||||||
|
|
||||||
currentDate.set(Calendar.DAY_OF_MONTH, 1);
|
currentDate.set(Calendar.DAY_OF_MONTH, 1);
|
||||||
currentDate.add(Calendar.MONTH, -nColumns + 2 - getDataOffset());
|
currentDate.add(Calendar.MONTH, -nColumns + 2 - getDataOffset());
|
||||||
|
|
||||||
@@ -193,11 +201,11 @@ public class FrequencyChart extends ScrollableChart
|
|||||||
|
|
||||||
private void drawColumn(Canvas canvas, RectF rect, GregorianCalendar date)
|
private void drawColumn(Canvas canvas, RectF rect, GregorianCalendar date)
|
||||||
{
|
{
|
||||||
Integer values[] = frequency.get(new Timestamp(date));
|
Integer[] values = frequency.get(new Timestamp(date));
|
||||||
float rowHeight = rect.height() / 8.0f;
|
float rowHeight = rect.height() / 8.0f;
|
||||||
prevRect.set(rect);
|
prevRect.set(rect);
|
||||||
|
|
||||||
Integer[] localeWeekdayList = DateUtils.getLocaleWeekdayList();
|
int[] localeWeekdayList = DateUtils.getWeekdaySequence(firstWeekday);
|
||||||
for (int j = 0; j < localeWeekdayList.length; j++)
|
for (int j = 0; j < localeWeekdayList.length; j++)
|
||||||
{
|
{
|
||||||
rect.set(0, 0, baseSize, baseSize);
|
rect.set(0, 0, baseSize, baseSize);
|
||||||
@@ -233,7 +241,7 @@ public class FrequencyChart extends ScrollableChart
|
|||||||
pText.setColor(textColor);
|
pText.setColor(textColor);
|
||||||
pGrid.setColor(gridColor);
|
pGrid.setColor(gridColor);
|
||||||
|
|
||||||
for (String day : DateUtils.getLocaleDayNames(Calendar.SHORT))
|
for (String day : DateUtils.getShortWeekdayNames(firstWeekday))
|
||||||
{
|
{
|
||||||
canvas.drawText(day, rGrid.right - columnWidth,
|
canvas.drawText(day, rGrid.right - columnWidth,
|
||||||
rGrid.top + rowHeight / 2 + 0.25f * em, pText);
|
rGrid.top + rowHeight / 2 + 0.25f * em, pText);
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ public class HistoryChart extends ScrollableChart
|
|||||||
|
|
||||||
private boolean isNumerical = false;
|
private boolean isNumerical = false;
|
||||||
|
|
||||||
|
private int firstWeekday = Calendar.SUNDAY;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private Controller controller;
|
private Controller controller;
|
||||||
|
|
||||||
@@ -210,6 +212,12 @@ public class HistoryChart extends ScrollableChart
|
|||||||
postInvalidate();
|
postInvalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFirstWeekday(int firstWeekday)
|
||||||
|
{
|
||||||
|
this.firstWeekday = firstWeekday;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
protected void initPaints()
|
protected void initPaints()
|
||||||
{
|
{
|
||||||
pTextHeader = new Paint();
|
pTextHeader = new Paint();
|
||||||
@@ -293,7 +301,7 @@ public class HistoryChart extends ScrollableChart
|
|||||||
{
|
{
|
||||||
float verticalOffset = pTextHeader.getFontSpacing() * 0.4f;
|
float verticalOffset = pTextHeader.getFontSpacing() * 0.4f;
|
||||||
|
|
||||||
for (String day : DateUtils.getLocaleDayNames(Calendar.SHORT))
|
for (String day : DateUtils.getShortWeekdayNames(firstWeekday))
|
||||||
{
|
{
|
||||||
location.offset(0, columnWidth);
|
location.offset(0, columnWidth);
|
||||||
canvas.drawText(day, location.left + headerTextOffset,
|
canvas.drawText(day, location.left + headerTextOffset,
|
||||||
@@ -375,7 +383,7 @@ public class HistoryChart extends ScrollableChart
|
|||||||
{
|
{
|
||||||
float width = 0;
|
float width = 0;
|
||||||
|
|
||||||
for (String w : DateUtils.getLocaleDayNames(Calendar.SHORT))
|
for (String w : DateUtils.getShortWeekdayNames(firstWeekday))
|
||||||
width = Math.max(width, pSquareFg.measureText(w));
|
width = Math.max(width, pSquareFg.measureText(w));
|
||||||
|
|
||||||
return width;
|
return width;
|
||||||
@@ -473,7 +481,7 @@ public class HistoryChart extends ScrollableChart
|
|||||||
int realWeekday =
|
int realWeekday =
|
||||||
DateUtils.getStartOfTodayCalendar().get(Calendar.DAY_OF_WEEK);
|
DateUtils.getStartOfTodayCalendar().get(Calendar.DAY_OF_WEEK);
|
||||||
todayPositionInColumn =
|
todayPositionInColumn =
|
||||||
(7 + realWeekday - baseDate.getFirstDayOfWeek()) % 7;
|
(7 + realWeekday - firstWeekday) % 7;
|
||||||
|
|
||||||
baseDate.add(Calendar.DAY_OF_YEAR, -nDays);
|
baseDate.add(Calendar.DAY_OF_YEAR, -nDays);
|
||||||
baseDate.add(Calendar.DAY_OF_YEAR, -todayPositionInColumn);
|
baseDate.add(Calendar.DAY_OF_YEAR, -todayPositionInColumn);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.isoron.uhabits.*;
|
|||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.activities.common.views.*;
|
import org.isoron.uhabits.activities.common.views.*;
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
|
import org.isoron.uhabits.core.preferences.*;
|
||||||
import org.isoron.uhabits.core.tasks.*;
|
import org.isoron.uhabits.core.tasks.*;
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
@@ -56,6 +57,9 @@ public class BarCard extends HabitCard
|
|||||||
|
|
||||||
private int bucketSize;
|
private int bucketSize;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Preferences prefs;
|
||||||
|
|
||||||
public BarCard(Context context)
|
public BarCard(Context context)
|
||||||
{
|
{
|
||||||
super(context);
|
super(context);
|
||||||
@@ -84,6 +88,12 @@ public class BarCard extends HabitCard
|
|||||||
|
|
||||||
private void init()
|
private void init()
|
||||||
{
|
{
|
||||||
|
Context appContext = getContext().getApplicationContext();
|
||||||
|
if (appContext instanceof HabitsApplication)
|
||||||
|
{
|
||||||
|
HabitsApplication app = (HabitsApplication) appContext;
|
||||||
|
prefs = app.getComponent().getPreferences();
|
||||||
|
}
|
||||||
inflate(getContext(), R.layout.show_habit_bar, this);
|
inflate(getContext(), R.layout.show_habit_bar, this);
|
||||||
ButterKnife.bind(this);
|
ButterKnife.bind(this);
|
||||||
boolSpinner.setSelection(1);
|
boolSpinner.setSelection(1);
|
||||||
@@ -120,8 +130,11 @@ public class BarCard extends HabitCard
|
|||||||
{
|
{
|
||||||
if (isCanceled()) return;
|
if (isCanceled()) return;
|
||||||
List<Checkmark> checkmarks;
|
List<Checkmark> checkmarks;
|
||||||
|
int firstWeekday = Calendar.SATURDAY;
|
||||||
|
if (prefs != null) firstWeekday = prefs.getFirstWeekday();
|
||||||
if (bucketSize == 1) checkmarks = habit.getCheckmarks().getAll();
|
if (bucketSize == 1) checkmarks = habit.getCheckmarks().getAll();
|
||||||
else checkmarks = habit.getCheckmarks().groupBy(getTruncateField(bucketSize));
|
else checkmarks = habit.getCheckmarks().groupBy(getTruncateField(bucketSize),
|
||||||
|
firstWeekday);
|
||||||
chart.setCheckmarks(checkmarks);
|
chart.setCheckmarks(checkmarks);
|
||||||
chart.setBucketSize(bucketSize);
|
chart.setBucketSize(bucketSize);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.isoron.uhabits.*;
|
|||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.activities.common.views.*;
|
import org.isoron.uhabits.activities.common.views.*;
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
|
import org.isoron.uhabits.core.preferences.*;
|
||||||
import org.isoron.uhabits.core.tasks.*;
|
import org.isoron.uhabits.core.tasks.*;
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
@@ -43,6 +44,9 @@ public class FrequencyCard extends HabitCard
|
|||||||
@BindView(R.id.frequencyChart)
|
@BindView(R.id.frequencyChart)
|
||||||
FrequencyChart chart;
|
FrequencyChart chart;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Preferences prefs;
|
||||||
|
|
||||||
public FrequencyCard(Context context)
|
public FrequencyCard(Context context)
|
||||||
{
|
{
|
||||||
super(context);
|
super(context);
|
||||||
@@ -63,6 +67,12 @@ public class FrequencyCard extends HabitCard
|
|||||||
|
|
||||||
private void init()
|
private void init()
|
||||||
{
|
{
|
||||||
|
Context appContext = getContext().getApplicationContext();
|
||||||
|
if (appContext instanceof HabitsApplication)
|
||||||
|
{
|
||||||
|
HabitsApplication app = (HabitsApplication) appContext;
|
||||||
|
prefs = app.getComponent().getPreferences();
|
||||||
|
}
|
||||||
inflate(getContext(), R.layout.show_habit_frequency, this);
|
inflate(getContext(), R.layout.show_habit_frequency, this);
|
||||||
ButterKnife.bind(this);
|
ButterKnife.bind(this);
|
||||||
if (isInEditMode()) initEditMode();
|
if (isInEditMode()) initEditMode();
|
||||||
@@ -84,6 +94,7 @@ public class FrequencyCard extends HabitCard
|
|||||||
if (isCanceled()) return;
|
if (isCanceled()) return;
|
||||||
RepetitionList reps = getHabit().getRepetitions();
|
RepetitionList reps = getHabit().getRepetitions();
|
||||||
HashMap<Timestamp, Integer[]> frequency = reps.getWeekdayFrequency();
|
HashMap<Timestamp, Integer[]> frequency = reps.getWeekdayFrequency();
|
||||||
|
if(prefs != null) chart.setFirstWeekday(prefs.getFirstWeekday());
|
||||||
chart.setFrequency(frequency);
|
chart.setFrequency(frequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.isoron.uhabits.*;
|
|||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.activities.common.views.*;
|
import org.isoron.uhabits.activities.common.views.*;
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
|
import org.isoron.uhabits.core.preferences.*;
|
||||||
import org.isoron.uhabits.core.tasks.*;
|
import org.isoron.uhabits.core.tasks.*;
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
@@ -41,9 +42,12 @@ public class HistoryCard extends HabitCard
|
|||||||
@BindView(R.id.title)
|
@BindView(R.id.title)
|
||||||
TextView title;
|
TextView title;
|
||||||
|
|
||||||
@NonNull
|
@Nullable
|
||||||
private Controller controller;
|
private Controller controller;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Preferences prefs;
|
||||||
|
|
||||||
public HistoryCard(Context context)
|
public HistoryCard(Context context)
|
||||||
{
|
{
|
||||||
super(context);
|
super(context);
|
||||||
@@ -59,16 +63,23 @@ public class HistoryCard extends HabitCard
|
|||||||
@OnClick(R.id.edit)
|
@OnClick(R.id.edit)
|
||||||
public void onClickEditButton()
|
public void onClickEditButton()
|
||||||
{
|
{
|
||||||
controller.onEditHistoryButtonClick();
|
if(controller != null) controller.onEditHistoryButtonClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setController(@NonNull Controller controller)
|
public void setController(@Nullable Controller controller)
|
||||||
{
|
{
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init()
|
private void init()
|
||||||
{
|
{
|
||||||
|
Context appContext = getContext().getApplicationContext();
|
||||||
|
if (appContext instanceof HabitsApplication)
|
||||||
|
{
|
||||||
|
HabitsApplication app = (HabitsApplication) appContext;
|
||||||
|
prefs = app.getComponent().getPreferences();
|
||||||
|
}
|
||||||
|
|
||||||
inflate(getContext(), R.layout.show_habit_history, this);
|
inflate(getContext(), R.layout.show_habit_history, this);
|
||||||
ButterKnife.bind(this);
|
ButterKnife.bind(this);
|
||||||
controller = new Controller() {};
|
controller = new Controller() {};
|
||||||
@@ -107,7 +118,8 @@ public class HistoryCard extends HabitCard
|
|||||||
public void doInBackground()
|
public void doInBackground()
|
||||||
{
|
{
|
||||||
if (isCanceled()) return;
|
if (isCanceled()) return;
|
||||||
int checkmarks[] = habit.getCheckmarks().getAllValues();
|
int[] checkmarks = habit.getCheckmarks().getAllValues();
|
||||||
|
if(prefs != null) chart.setFirstWeekday(prefs.getFirstWeekday());
|
||||||
chart.setCheckmarks(checkmarks);
|
chart.setCheckmarks(checkmarks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -143,9 +143,12 @@ public class ScoreCard extends HabitCard
|
|||||||
if (isCanceled()) return;
|
if (isCanceled()) return;
|
||||||
List<Score> scores;
|
List<Score> scores;
|
||||||
ScoreList scoreList = getHabit().getScores();
|
ScoreList scoreList = getHabit().getScores();
|
||||||
|
int firstWeekday = Calendar.SATURDAY;
|
||||||
|
if (prefs != null) firstWeekday = prefs.getFirstWeekday();
|
||||||
|
Log.d("ScoreCard", "firstWeekday="+firstWeekday);
|
||||||
|
|
||||||
if (bucketSize == 1) scores = scoreList.toList();
|
if (bucketSize == 1) scores = scoreList.toList();
|
||||||
else scores = scoreList.groupBy(getTruncateField(bucketSize));
|
else scores = scoreList.groupBy(getTruncateField(bucketSize), firstWeekday);
|
||||||
|
|
||||||
chart.setScores(scores);
|
chart.setScores(scores);
|
||||||
chart.setBucketSize(bucketSize);
|
chart.setBucketSize(bucketSize);
|
||||||
|
|||||||
@@ -32,9 +32,12 @@ import org.isoron.uhabits.R;
|
|||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.core.preferences.*;
|
import org.isoron.uhabits.core.preferences.*;
|
||||||
import org.isoron.uhabits.core.ui.*;
|
import org.isoron.uhabits.core.ui.*;
|
||||||
|
import org.isoron.uhabits.core.utils.*;
|
||||||
import org.isoron.uhabits.notifications.*;
|
import org.isoron.uhabits.notifications.*;
|
||||||
import org.isoron.uhabits.widgets.*;
|
import org.isoron.uhabits.widgets.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import static android.media.RingtoneManager.*;
|
import static android.media.RingtoneManager.*;
|
||||||
import static android.os.Build.VERSION.*;
|
import static android.os.Build.VERSION.*;
|
||||||
import static org.isoron.uhabits.activities.habits.list.ListHabitsScreenKt.*;
|
import static org.isoron.uhabits.activities.habits.list.ListHabitsScreenKt.*;
|
||||||
@@ -143,6 +146,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
devCategory.setVisible(false);
|
devCategory.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateWeekdayPreference();
|
||||||
|
|
||||||
if (SDK_INT < Build.VERSION_CODES.O)
|
if (SDK_INT < Build.VERSION_CODES.O)
|
||||||
findPreference("reminderCustomize").setVisible(false);
|
findPreference("reminderCustomize").setVisible(false);
|
||||||
else
|
else
|
||||||
@@ -154,6 +159,19 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
updateSync();
|
updateSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateWeekdayPreference()
|
||||||
|
{
|
||||||
|
if (prefs == null) return;
|
||||||
|
ListPreference weekdayPref = (ListPreference) findPreference("pref_first_weekday");
|
||||||
|
int currentFirstWeekday = prefs.getFirstWeekday();
|
||||||
|
String[] dayNames = DateUtils.getLongWeekdayNames(Calendar.SATURDAY);
|
||||||
|
String[] dayValues = {"7", "1", "2", "3", "4", "5", "6"};
|
||||||
|
weekdayPref.setEntries(dayNames);
|
||||||
|
weekdayPref.setEntryValues(dayValues);
|
||||||
|
weekdayPref.setDefaultValue(Integer.toString(currentFirstWeekday));
|
||||||
|
weekdayPref.setSummary(dayNames[currentFirstWeekday % 7]);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
|
||||||
String key)
|
String key)
|
||||||
@@ -163,6 +181,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
Log.d("SettingsFragment", "updating widgets");
|
Log.d("SettingsFragment", "updating widgets");
|
||||||
widgetUpdater.updateWidgets();
|
widgetUpdater.updateWidgets();
|
||||||
}
|
}
|
||||||
|
if (key.equals("pref_first_weekday")) updateWeekdayPreference();
|
||||||
BackupManager.dataChanged("org.isoron.uhabits");
|
BackupManager.dataChanged("org.isoron.uhabits");
|
||||||
updateSync();
|
updateSync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ public class AndroidDateUtils
|
|||||||
|
|
||||||
public static String formatWeekdayList(Context context, boolean weekday[])
|
public static String formatWeekdayList(Context context, boolean weekday[])
|
||||||
{
|
{
|
||||||
String shortDayNames[] = org.isoron.uhabits.core.utils.DateUtils.getShortDayNames();
|
String shortDayNames[] = org.isoron.uhabits.core.utils.DateUtils.getShortWeekdayNames(Calendar.SATURDAY);
|
||||||
String longDayNames[] = org.isoron.uhabits.core.utils.DateUtils.getLongDayNames();
|
String longDayNames[] = org.isoron.uhabits.core.utils.DateUtils.getLongWeekdayNames(Calendar.SATURDAY);
|
||||||
StringBuilder buffer = new StringBuilder();
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
|
|||||||
private HabitList habits;
|
private HabitList habits;
|
||||||
|
|
||||||
private Preferences preferences;
|
private Preferences preferences;
|
||||||
|
|
||||||
private WidgetPreferences widgetPrefs;
|
private WidgetPreferences widgetPrefs;
|
||||||
|
|
||||||
public static void updateAppWidget(@NonNull AppWidgetManager manager,
|
public static void updateAppWidget(@NonNull AppWidgetManager manager,
|
||||||
@@ -195,4 +196,9 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
|
|||||||
preferences = app.getComponent().getPreferences();
|
preferences = app.getComponent().getPreferences();
|
||||||
widgetPrefs = app.getComponent().getWidgetPreferences();
|
widgetPrefs = app.getComponent().getWidgetPreferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Preferences getPreferences()
|
||||||
|
{
|
||||||
|
return preferences;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ import org.isoron.uhabits.widgets.views.*
|
|||||||
class FrequencyWidget(
|
class FrequencyWidget(
|
||||||
context: Context,
|
context: Context,
|
||||||
widgetId: Int,
|
widgetId: Int,
|
||||||
private val habit: Habit
|
private val habit: Habit,
|
||||||
|
private val firstWeekday: Int
|
||||||
) : BaseWidget(context, widgetId) {
|
) : BaseWidget(context, widgetId) {
|
||||||
|
|
||||||
override fun getOnClickPendingIntent(context: Context) =
|
override fun getOnClickPendingIntent(context: Context) =
|
||||||
@@ -40,6 +41,7 @@ class FrequencyWidget(
|
|||||||
widgetView.setTitle(habit.name)
|
widgetView.setTitle(habit.name)
|
||||||
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
||||||
(widgetView.dataView as FrequencyChart).apply {
|
(widgetView.dataView as FrequencyChart).apply {
|
||||||
|
setFirstWeekday(firstWeekday)
|
||||||
setColor(PaletteUtils.getColor(context, habit.color))
|
setColor(PaletteUtils.getColor(context, habit.color))
|
||||||
setFrequency(habit.repetitions.weekdayFrequency)
|
setFrequency(habit.repetitions.weekdayFrequency)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,10 @@ import android.content.*
|
|||||||
class FrequencyWidgetProvider : BaseWidgetProvider() {
|
class FrequencyWidgetProvider : BaseWidgetProvider() {
|
||||||
override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
|
override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
|
||||||
val habits = getHabitsFromWidgetId(id)
|
val habits = getHabitsFromWidgetId(id)
|
||||||
if (habits.size == 1) return FrequencyWidget(context, id, habits[0])
|
if (habits.size == 1) return FrequencyWidget(context,
|
||||||
|
id,
|
||||||
|
habits[0],
|
||||||
|
preferences.firstWeekday)
|
||||||
else return StackWidget(context, id, StackWidgetType.FREQUENCY, habits)
|
else return StackWidget(context, id, StackWidgetType.FREQUENCY, habits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ import org.isoron.uhabits.widgets.views.*
|
|||||||
class HistoryWidget(
|
class HistoryWidget(
|
||||||
context: Context,
|
context: Context,
|
||||||
id: Int,
|
id: Int,
|
||||||
private val habit: Habit
|
private val habit: Habit,
|
||||||
|
private val firstWeekday: Int
|
||||||
) : BaseWidget(context, id) {
|
) : BaseWidget(context, id) {
|
||||||
|
|
||||||
override fun getOnClickPendingIntent(context: Context): PendingIntent {
|
override fun getOnClickPendingIntent(context: Context): PendingIntent {
|
||||||
@@ -41,6 +42,7 @@ class HistoryWidget(
|
|||||||
val widgetView = view as GraphWidgetView
|
val widgetView = view as GraphWidgetView
|
||||||
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
||||||
(widgetView.dataView as HistoryChart).apply {
|
(widgetView.dataView as HistoryChart).apply {
|
||||||
|
setFirstWeekday(firstWeekday)
|
||||||
setColor(PaletteUtils.getColor(context, habit.color))
|
setColor(PaletteUtils.getColor(context, habit.color))
|
||||||
setCheckmarks(habit.checkmarks.allValues)
|
setCheckmarks(habit.checkmarks.allValues)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ import android.content.*
|
|||||||
class HistoryWidgetProvider : BaseWidgetProvider() {
|
class HistoryWidgetProvider : BaseWidgetProvider() {
|
||||||
override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
|
override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
|
||||||
val habits = getHabitsFromWidgetId(id)
|
val habits = getHabitsFromWidgetId(id)
|
||||||
if (habits.size == 1) return HistoryWidget(context, id, habits[0])
|
if (habits.size == 1) return HistoryWidget(context,
|
||||||
|
id,
|
||||||
|
habits[0],
|
||||||
|
preferences.firstWeekday)
|
||||||
else return StackWidget(context, id, StackWidgetType.HISTORY, habits)
|
else return StackWidget(context, id, StackWidgetType.HISTORY, habits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ class ScoreWidget(
|
|||||||
val size = ScoreCard.BUCKET_SIZES[prefs.defaultScoreSpinnerPosition]
|
val size = ScoreCard.BUCKET_SIZES[prefs.defaultScoreSpinnerPosition]
|
||||||
val scores = when(size) {
|
val scores = when(size) {
|
||||||
1 -> habit.scores.toList()
|
1 -> habit.scores.toList()
|
||||||
else -> habit.scores.groupBy(ScoreCard.getTruncateField(size))
|
else -> habit.scores.groupBy(ScoreCard.getTruncateField(size),
|
||||||
|
prefs.firstWeekday)
|
||||||
}
|
}
|
||||||
|
|
||||||
val widgetView = view as GraphWidgetView
|
val widgetView = view as GraphWidgetView
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import android.widget.*;
|
|||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
|
import org.isoron.uhabits.core.preferences.*;
|
||||||
import org.isoron.uhabits.core.utils.*;
|
import org.isoron.uhabits.core.utils.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -87,18 +88,19 @@ class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private BaseWidget constructWidget(@NonNull Habit habit)
|
private BaseWidget constructWidget(@NonNull Habit habit,
|
||||||
|
@NonNull Preferences prefs)
|
||||||
{
|
{
|
||||||
switch (widgetType)
|
switch (widgetType)
|
||||||
{
|
{
|
||||||
case CHECKMARK:
|
case CHECKMARK:
|
||||||
return new CheckmarkWidget(context, widgetId, habit);
|
return new CheckmarkWidget(context, widgetId, habit);
|
||||||
case FREQUENCY:
|
case FREQUENCY:
|
||||||
return new FrequencyWidget(context, widgetId, habit);
|
return new FrequencyWidget(context, widgetId, habit, prefs.getFirstWeekday());
|
||||||
case SCORE:
|
case SCORE:
|
||||||
return new ScoreWidget(context, widgetId, habit);
|
return new ScoreWidget(context, widgetId, habit);
|
||||||
case HISTORY:
|
case HISTORY:
|
||||||
return new HistoryWidget(context, widgetId, habit);
|
return new HistoryWidget(context, widgetId, habit, prefs.getFirstWeekday());
|
||||||
case STREAKS:
|
case STREAKS:
|
||||||
return new StreakWidget(context, widgetId, habit);
|
return new StreakWidget(context, widgetId, habit);
|
||||||
}
|
}
|
||||||
@@ -136,6 +138,7 @@ class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory
|
|||||||
Log.i("StackRemoteViewsFactory", "onDataSetChanged started");
|
Log.i("StackRemoteViewsFactory", "onDataSetChanged started");
|
||||||
|
|
||||||
HabitsApplication app = (HabitsApplication) context.getApplicationContext();
|
HabitsApplication app = (HabitsApplication) context.getApplicationContext();
|
||||||
|
Preferences prefs = app.getComponent().getPreferences();
|
||||||
HabitList habitList = app.getComponent().getHabitList();
|
HabitList habitList = app.getComponent().getHabitList();
|
||||||
Bundle options = AppWidgetManager.getInstance(context).getAppWidgetOptions(widgetId);
|
Bundle options = AppWidgetManager.getInstance(context).getAppWidgetOptions(widgetId);
|
||||||
ArrayList<RemoteViews> newRemoteViews = new ArrayList<>();
|
ArrayList<RemoteViews> newRemoteViews = new ArrayList<>();
|
||||||
@@ -147,7 +150,7 @@ class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory
|
|||||||
Habit h = habitList.getById(id);
|
Habit h = habitList.getById(id);
|
||||||
if (h == null) throw new HabitNotFoundException();
|
if (h == null) throw new HabitNotFoundException();
|
||||||
|
|
||||||
BaseWidget widget = constructWidget(h);
|
BaseWidget widget = constructWidget(h, prefs);
|
||||||
widget.setDimensions(getDimensionsFromOptions(context, options));
|
widget.setDimensions(getDimensionsFromOptions(context, options));
|
||||||
|
|
||||||
RemoteViews landscapeViews = widget.getLandscapeRemoteViews();
|
RemoteViews landscapeViews = widget.getLandscapeRemoteViews();
|
||||||
|
|||||||
@@ -242,5 +242,6 @@
|
|||||||
<string name="database">Database</string>
|
<string name="database">Database</string>
|
||||||
<string name="widget_opacity_title">Widget opacity</string>
|
<string name="widget_opacity_title">Widget opacity</string>
|
||||||
<string name="widget_opacity_description">Makes widgets more transparent or more opaque in your home screen.</string>
|
<string name="widget_opacity_description">Makes widgets more transparent or more opaque in your home screen.</string>
|
||||||
|
<string name="first_day_of_the_week">First day of the week</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -55,6 +55,11 @@
|
|||||||
android:title="@string/widget_opacity_title"
|
android:title="@string/widget_opacity_title"
|
||||||
app:iconSpaceReserved="false" />
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:key="pref_first_weekday"
|
||||||
|
android:title="@string/first_day_of_the_week"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ public abstract class CheckmarkList
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public List<Checkmark> groupBy(DateUtils.TruncateField field)
|
public List<Checkmark> groupBy(DateUtils.TruncateField field, int firstWeekday)
|
||||||
{
|
{
|
||||||
List<Checkmark> checks = getAll();
|
List<Checkmark> checks = getAll();
|
||||||
|
|
||||||
@@ -422,7 +422,7 @@ public abstract class CheckmarkList
|
|||||||
|
|
||||||
for (Checkmark rep : checks)
|
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))
|
if (count == 0 || !truncatedTimestamps[count - 1].equals(tt))
|
||||||
truncatedTimestamps[count++] = tt;
|
truncatedTimestamps[count++] = tt;
|
||||||
|
|
||||||
|
|||||||
@@ -129,10 +129,10 @@ public abstract class ScoreList implements Iterable<Score>
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Score> groupBy(DateUtils.TruncateField field)
|
public List<Score> groupBy(DateUtils.TruncateField field, int firstWeekday)
|
||||||
{
|
{
|
||||||
computeAll();
|
computeAll();
|
||||||
HashMap<Timestamp, ArrayList<Double>> groups = getGroupedValues(field);
|
HashMap<Timestamp, ArrayList<Double>> groups = getGroupedValues(field, firstWeekday);
|
||||||
List<Score> scores = groupsToAvgScores(groups);
|
List<Score> scores = groupsToAvgScores(groups);
|
||||||
Collections.sort(scores, (s1, s2) -> s2.compareNewer(s1));
|
Collections.sort(scores, (s1, s2) -> s2.compareNewer(s1));
|
||||||
return scores;
|
return scores;
|
||||||
@@ -293,14 +293,18 @@ public abstract class ScoreList implements Iterable<Score>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@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<>();
|
HashMap<Timestamp, ArrayList<Double>> groups = new HashMap<>();
|
||||||
|
|
||||||
for (Score s : this)
|
for (Score s : this)
|
||||||
{
|
{
|
||||||
Timestamp groupTimestamp = new Timestamp(
|
Timestamp groupTimestamp = new Timestamp(
|
||||||
DateUtils.truncate(field, s.getTimestamp().getUnixTime()));
|
DateUtils.truncate(
|
||||||
|
field,
|
||||||
|
s.getTimestamp().getUnixTime(),
|
||||||
|
firstWeekday));
|
||||||
|
|
||||||
if (!groups.containsKey(groupTimestamp))
|
if (!groups.containsKey(groupTimestamp))
|
||||||
groups.put(groupTimestamp, new ArrayList<>());
|
groups.put(groupTimestamp, new ArrayList<>());
|
||||||
|
|||||||
@@ -143,13 +143,17 @@ public final class Timestamp
|
|||||||
return DateFormats.getCSVDateFormat().format(new Date(unixTime));
|
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()
|
public int getWeekday()
|
||||||
{
|
{
|
||||||
return toCalendar().get(DAY_OF_WEEK) % 7;
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -333,6 +333,19 @@ public class Preferences
|
|||||||
return Integer.parseInt(storage.getString("pref_widget_opacity", "102"));
|
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
|
public interface Listener
|
||||||
{
|
{
|
||||||
default void onCheckmarkSequenceChanged()
|
default void onCheckmarkSequenceChanged()
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ package org.isoron.uhabits.core.utils;
|
|||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
|
import org.jetbrains.annotations.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -73,23 +74,6 @@ public abstract class DateUtils
|
|||||||
return day;
|
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()
|
public static long getLocalTime()
|
||||||
{
|
{
|
||||||
if (fixedLocalTime != null) return fixedLocalTime;
|
if (fixedLocalTime != null) return fixedLocalTime;
|
||||||
@@ -100,19 +84,25 @@ public abstract class DateUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array with weekday names starting according to locale settings,
|
* Returns an array of strings with the names for each day of the week,
|
||||||
* e.g. [Mo,Di,Mi,Do,Fr,Sa,So] in Germany
|
* 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];
|
String[] days = new String[7];
|
||||||
|
|
||||||
Calendar calendar = new GregorianCalendar();
|
Calendar calendar = new GregorianCalendar();
|
||||||
calendar.set(DAY_OF_WEEK, calendar.getFirstDayOfWeek());
|
calendar.set(DAY_OF_WEEK, firstWeekday);
|
||||||
for (int i = 0; i < days.length; i++)
|
for (int i = 0; i < days.length; i++) {
|
||||||
{
|
|
||||||
days[i] = calendar.getDisplayName(DAY_OF_WEEK, format,
|
days[i] = calendar.getDisplayName(DAY_OF_WEEK, format,
|
||||||
getLocale());
|
getLocale());
|
||||||
calendar.add(DAY_OF_MONTH, 1);
|
calendar.add(DAY_OF_MONTH, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,30 +110,63 @@ public abstract class DateUtils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array with week days numbers starting according to locale
|
* Returns a vector of exactly seven integers, where the first integer is
|
||||||
* settings, e.g. [2,3,4,5,6,7,1] in Europe
|
* 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];
|
return new int[]
|
||||||
Calendar calendar = new GregorianCalendar();
|
|
||||||
calendar.set(DAY_OF_WEEK, calendar.getFirstDayOfWeek());
|
|
||||||
for (int i = 0; i < dayNumbers.length; i++)
|
|
||||||
{
|
{
|
||||||
dayNumbers[i] = calendar.get(DAY_OF_WEEK);
|
(firstWeekday - 1) % 7 + 1,
|
||||||
calendar.add(DAY_OF_MONTH, 1);
|
(firstWeekday) % 7 + 1,
|
||||||
}
|
(firstWeekday + 1) % 7 + 1,
|
||||||
return dayNumbers;
|
(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
|
@NonNull
|
||||||
@@ -206,10 +229,13 @@ public abstract class DateUtils
|
|||||||
return Locale.getDefault();
|
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);
|
GregorianCalendar cal = DateUtils.getCalendar(timestamp);
|
||||||
|
|
||||||
|
|
||||||
switch (field)
|
switch (field)
|
||||||
{
|
{
|
||||||
case MONTH:
|
case MONTH:
|
||||||
@@ -217,7 +243,6 @@ public abstract class DateUtils
|
|||||||
return cal.getTimeInMillis();
|
return cal.getTimeInMillis();
|
||||||
|
|
||||||
case WEEK_NUMBER:
|
case WEEK_NUMBER:
|
||||||
int firstWeekday = cal.getFirstDayOfWeek();
|
|
||||||
int weekday = cal.get(DAY_OF_WEEK);
|
int weekday = cal.get(DAY_OF_WEEK);
|
||||||
int delta = weekday - firstWeekday;
|
int delta = weekday - firstWeekday;
|
||||||
if (delta < 0) delta += 7;
|
if (delta < 0) delta += 7;
|
||||||
|
|||||||
@@ -388,20 +388,20 @@ public class CheckmarkListTest extends BaseUnitTest
|
|||||||
Habit habit = fixtures.createLongNumericalHabit(timestamp(2014, JUNE, 1));
|
Habit habit = fixtures.createLongNumericalHabit(timestamp(2014, JUNE, 1));
|
||||||
CheckmarkList checkmarks = habit.getCheckmarks();
|
CheckmarkList checkmarks = habit.getCheckmarks();
|
||||||
|
|
||||||
List<Checkmark> byMonth = checkmarks.groupBy(MONTH);
|
List<Checkmark> byMonth = checkmarks.groupBy(MONTH, Calendar.SATURDAY);
|
||||||
assertThat(byMonth.size(), equalTo(25)); // from 2013-01-01 to 2015-01-01
|
assertThat(byMonth.size(), equalTo(25)); // from 2013-01-01 to 2015-01-01
|
||||||
assertThat(byMonth.get(0), equalTo(new Checkmark(timestamp(2015, JANUARY, 1), 0)));
|
assertThat(byMonth.get(0), equalTo(new Checkmark(timestamp(2015, JANUARY, 1), 0)));
|
||||||
assertThat(byMonth.get(6), equalTo(new Checkmark(timestamp(2014, JULY, 1), 0)));
|
assertThat(byMonth.get(6), equalTo(new Checkmark(timestamp(2014, JULY, 1), 0)));
|
||||||
assertThat(byMonth.get(12), equalTo(new Checkmark(timestamp(2014, JANUARY, 1), 1706)));
|
assertThat(byMonth.get(12), equalTo(new Checkmark(timestamp(2014, JANUARY, 1), 1706)));
|
||||||
assertThat(byMonth.get(18), equalTo(new Checkmark(timestamp(2013, JULY, 1), 1379)));
|
assertThat(byMonth.get(18), equalTo(new Checkmark(timestamp(2013, JULY, 1), 1379)));
|
||||||
|
|
||||||
List<Checkmark> byQuarter = checkmarks.groupBy(QUARTER);
|
List<Checkmark> byQuarter = checkmarks.groupBy(QUARTER, Calendar.SATURDAY);
|
||||||
assertThat(byQuarter.size(), equalTo(9)); // from 2013-Q1 to 2015-Q1
|
assertThat(byQuarter.size(), equalTo(9)); // from 2013-Q1 to 2015-Q1
|
||||||
assertThat(byQuarter.get(0), equalTo(new Checkmark(timestamp(2015, JANUARY, 1), 0)));
|
assertThat(byQuarter.get(0), equalTo(new Checkmark(timestamp(2015, JANUARY, 1), 0)));
|
||||||
assertThat(byQuarter.get(4), equalTo(new Checkmark(timestamp(2014, JANUARY, 1), 4964)));
|
assertThat(byQuarter.get(4), equalTo(new Checkmark(timestamp(2014, JANUARY, 1), 4964)));
|
||||||
assertThat(byQuarter.get(8), equalTo(new Checkmark(timestamp(2013, JANUARY, 1), 4975)));
|
assertThat(byQuarter.get(8), equalTo(new Checkmark(timestamp(2013, JANUARY, 1), 4975)));
|
||||||
|
|
||||||
List<Checkmark> byYear = checkmarks.groupBy(YEAR);
|
List<Checkmark> byYear = checkmarks.groupBy(YEAR, Calendar.SATURDAY);
|
||||||
assertThat(byYear.size(), equalTo(3)); // from 2013 to 2015
|
assertThat(byYear.size(), equalTo(3)); // from 2013 to 2015
|
||||||
assertThat(byYear.get(0), equalTo(new Checkmark(timestamp(2015, JANUARY, 1), 0)));
|
assertThat(byYear.get(0), equalTo(new Checkmark(timestamp(2015, JANUARY, 1), 0)));
|
||||||
assertThat(byYear.get(1), equalTo(new Checkmark(timestamp(2014, JANUARY, 1), 8227)));
|
assertThat(byYear.get(1), equalTo(new Checkmark(timestamp(2014, JANUARY, 1), 8227)));
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ public class ScoreListTest extends BaseUnitTest
|
|||||||
{
|
{
|
||||||
Habit habit = fixtures.createLongHabit();
|
Habit habit = fixtures.createLongHabit();
|
||||||
List<Score> list =
|
List<Score> list =
|
||||||
habit.getScores().groupBy(DateUtils.TruncateField.MONTH);
|
habit.getScores().groupBy(DateUtils.TruncateField.MONTH, Calendar.SATURDAY);
|
||||||
|
|
||||||
assertThat(list.size(), equalTo(5));
|
assertThat(list.size(), equalTo(5));
|
||||||
assertThat(list.get(0).getValue(), closeTo(0.653659, E));
|
assertThat(list.get(0).getValue(), closeTo(0.653659, E));
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ import static org.isoron.uhabits.core.utils.DateUtils.removeTimezone;
|
|||||||
|
|
||||||
public class DateUtilsTest extends BaseUnitTest
|
public class DateUtilsTest extends BaseUnitTest
|
||||||
{
|
{
|
||||||
|
|
||||||
|
int firstWeekday = SUNDAY;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@Override
|
@Override
|
||||||
public void setUp() throws Exception
|
public void setUp() throws Exception
|
||||||
@@ -61,18 +64,29 @@ public class DateUtilsTest extends BaseUnitTest
|
|||||||
long t1 = unixTime(2015, Calendar.JANUARY, 16);
|
long t1 = unixTime(2015, Calendar.JANUARY, 16);
|
||||||
long t2 = unixTime(2015, Calendar.JANUARY, 17);
|
long t2 = unixTime(2015, Calendar.JANUARY, 17);
|
||||||
|
|
||||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||||
|
|
||||||
expected = unixTime(2015, Calendar.JANUARY, 18);
|
expected = unixTime(2015, Calendar.JANUARY, 18);
|
||||||
t0 = unixTime(2015, Calendar.JANUARY, 18);
|
t0 = unixTime(2015, Calendar.JANUARY, 18);
|
||||||
t1 = unixTime(2015, Calendar.JANUARY, 19);
|
t1 = unixTime(2015, Calendar.JANUARY, 19);
|
||||||
t2 = unixTime(2015, Calendar.JANUARY, 24);
|
t2 = unixTime(2015, Calendar.JANUARY, 24);
|
||||||
|
|
||||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||||
|
|
||||||
|
|
||||||
|
firstWeekday = WEDNESDAY;
|
||||||
|
expected = unixTime(2015, Calendar.JANUARY, 7);
|
||||||
|
t0 = unixTime(2015, Calendar.JANUARY, 7);
|
||||||
|
t1 = unixTime(2015, Calendar.JANUARY, 9);
|
||||||
|
t2 = unixTime(2015, Calendar.JANUARY, 13);
|
||||||
|
|
||||||
|
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||||
|
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||||
|
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -85,18 +99,18 @@ public class DateUtilsTest extends BaseUnitTest
|
|||||||
|
|
||||||
DateUtils.TruncateField field = DateUtils.TruncateField.MONTH;
|
DateUtils.TruncateField field = DateUtils.TruncateField.MONTH;
|
||||||
|
|
||||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||||
|
|
||||||
expected = unixTime(2016, DECEMBER, 1);
|
expected = unixTime(2016, DECEMBER, 1);
|
||||||
t0 = unixTime(2016, DECEMBER, 1);
|
t0 = unixTime(2016, DECEMBER, 1);
|
||||||
t1 = unixTime(2016, DECEMBER, 15);
|
t1 = unixTime(2016, DECEMBER, 15);
|
||||||
t2 = unixTime(2016, DECEMBER, 31);
|
t2 = unixTime(2016, DECEMBER, 31);
|
||||||
|
|
||||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -109,18 +123,18 @@ public class DateUtilsTest extends BaseUnitTest
|
|||||||
long t1 = unixTime(2016, FEBRUARY, 15);
|
long t1 = unixTime(2016, FEBRUARY, 15);
|
||||||
long t2 = unixTime(2016, MARCH, 30);
|
long t2 = unixTime(2016, MARCH, 30);
|
||||||
|
|
||||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||||
|
|
||||||
expected = unixTime(2016, APRIL, 1);
|
expected = unixTime(2016, APRIL, 1);
|
||||||
t0 = unixTime(2016, APRIL, 1);
|
t0 = unixTime(2016, APRIL, 1);
|
||||||
t1 = unixTime(2016, MAY, 30);
|
t1 = unixTime(2016, MAY, 30);
|
||||||
t2 = unixTime(2016, JUNE, 20);
|
t2 = unixTime(2016, JUNE, 20);
|
||||||
|
|
||||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -133,18 +147,18 @@ public class DateUtilsTest extends BaseUnitTest
|
|||||||
long t1 = unixTime(2016, FEBRUARY, 25);
|
long t1 = unixTime(2016, FEBRUARY, 25);
|
||||||
long t2 = unixTime(2016, DECEMBER, 31);
|
long t2 = unixTime(2016, DECEMBER, 31);
|
||||||
|
|
||||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||||
|
|
||||||
expected = unixTime(2017, JANUARY, 1);
|
expected = unixTime(2017, JANUARY, 1);
|
||||||
t0 = unixTime(2017, JANUARY, 1);
|
t0 = unixTime(2017, JANUARY, 1);
|
||||||
t1 = unixTime(2017, MAY, 30);
|
t1 = unixTime(2017, MAY, 30);
|
||||||
t2 = unixTime(2017, DECEMBER, 31);
|
t2 = unixTime(2017, DECEMBER, 31);
|
||||||
|
|
||||||
assertThat(DateUtils.truncate(field, t0), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t0, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t1), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t1, firstWeekday), equalTo(expected));
|
||||||
assertThat(DateUtils.truncate(field, t2), equalTo(expected));
|
assertThat(DateUtils.truncate(field, t2, firstWeekday), equalTo(expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user