Skip to content

Commit 3d4fc74

Browse files
authored
Merge pull request #619 from rivaldi8/bugfix-617-weekly-exports-listed-wrong-day
Bugfix 617: Weekly scheduled exports are listed with the wrong day Fixes #617
2 parents f153a6f + 5911aa6 commit 3d4fc74

3 files changed

Lines changed: 158 additions & 101 deletions

File tree

app/src/main/java/org/gnucash/android/db/adapter/RecurrenceDbAdapter.java

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,17 @@
2020
import android.database.sqlite.SQLiteDatabase;
2121
import android.database.sqlite.SQLiteStatement;
2222
import android.support.annotation.NonNull;
23+
import android.support.annotation.Nullable;
2324

2425
import org.gnucash.android.app.GnuCashApplication;
2526
import org.gnucash.android.model.PeriodType;
2627
import org.gnucash.android.model.Recurrence;
2728

2829
import java.sql.Timestamp;
30+
import java.util.ArrayList;
31+
import java.util.Calendar;
32+
import java.util.Collections;
33+
import java.util.List;
2934

3035
import static org.gnucash.android.db.DatabaseSchema.RecurrenceEntry;
3136

@@ -58,7 +63,7 @@ public Recurrence buildModelInstance(@NonNull Cursor cursor) {
5863
long multiplier = cursor.getLong(cursor.getColumnIndexOrThrow(RecurrenceEntry.COLUMN_MULTIPLIER));
5964
String periodStart = cursor.getString(cursor.getColumnIndexOrThrow(RecurrenceEntry.COLUMN_PERIOD_START));
6065
String periodEnd = cursor.getString(cursor.getColumnIndexOrThrow(RecurrenceEntry.COLUMN_PERIOD_END));
61-
String byDay = cursor.getString(cursor.getColumnIndexOrThrow(RecurrenceEntry.COLUMN_BYDAY));
66+
String byDays = cursor.getString(cursor.getColumnIndexOrThrow(RecurrenceEntry.COLUMN_BYDAY));
6267

6368
PeriodType periodType = PeriodType.valueOf(type);
6469
periodType.setMultiplier((int) multiplier);
@@ -67,7 +72,7 @@ public Recurrence buildModelInstance(@NonNull Cursor cursor) {
6772
recurrence.setPeriodStart(Timestamp.valueOf(periodStart));
6873
if (periodEnd != null)
6974
recurrence.setPeriodEnd(Timestamp.valueOf(periodEnd));
70-
recurrence.setByDay(byDay);
75+
recurrence.setByDays(stringToByDays(byDays));
7176

7277
populateBaseModelAttributes(cursor, recurrence);
7378

@@ -79,8 +84,8 @@ public Recurrence buildModelInstance(@NonNull Cursor cursor) {
7984
stmt.clearBindings();
8085
stmt.bindLong(1, recurrence.getPeriodType().getMultiplier());
8186
stmt.bindString(2, recurrence.getPeriodType().name());
82-
if (recurrence.getByDay() != null)
83-
stmt.bindString(3, recurrence.getByDay());
87+
if (!recurrence.getByDays().isEmpty())
88+
stmt.bindString(3, byDaysToString(recurrence.getByDays()));
8489
//recurrence should always have a start date
8590
stmt.bindString(4, recurrence.getPeriodStart().toString());
8691

@@ -90,4 +95,87 @@ public Recurrence buildModelInstance(@NonNull Cursor cursor) {
9095

9196
return stmt;
9297
}
98+
99+
/**
100+
* Converts a list of days of week as Calendar constants to an String for
101+
* storing in the database.
102+
*
103+
* @param byDays list of days of week constants from Calendar
104+
* @return String of days of the week or null if {@code byDays} was empty
105+
*/
106+
private static @NonNull String byDaysToString(@NonNull List<Integer> byDays) {
107+
StringBuilder builder = new StringBuilder();
108+
for (int day : byDays) {
109+
switch (day) {
110+
case Calendar.MONDAY:
111+
builder.append("MO");
112+
break;
113+
case Calendar.TUESDAY:
114+
builder.append("TU");
115+
break;
116+
case Calendar.WEDNESDAY:
117+
builder.append("WE");
118+
break;
119+
case Calendar.THURSDAY:
120+
builder.append("TH");
121+
break;
122+
case Calendar.FRIDAY:
123+
builder.append("FR");
124+
break;
125+
case Calendar.SATURDAY:
126+
builder.append("SA");
127+
break;
128+
case Calendar.SUNDAY:
129+
builder.append("SU");
130+
break;
131+
default:
132+
throw new RuntimeException("bad day of week: " + day);
133+
}
134+
builder.append(",");
135+
}
136+
builder.deleteCharAt(builder.length()-1);
137+
return builder.toString();
138+
}
139+
140+
/**
141+
* Converts a String with the comma-separated days of the week into a
142+
* list of Calendar constants.
143+
*
144+
* @param byDaysString String with comma-separated days fo the week
145+
* @return list of days of the week as Calendar constants.
146+
*/
147+
private static @NonNull List<Integer> stringToByDays(@Nullable String byDaysString) {
148+
if (byDaysString == null)
149+
return Collections.emptyList();
150+
151+
List<Integer> byDaysList = new ArrayList<>();
152+
for (String day : byDaysString.split(",")) {
153+
switch (day) {
154+
case "MO":
155+
byDaysList.add(Calendar.MONDAY);
156+
break;
157+
case "TU":
158+
byDaysList.add(Calendar.TUESDAY);
159+
break;
160+
case "WE":
161+
byDaysList.add(Calendar.WEDNESDAY);
162+
break;
163+
case "TH":
164+
byDaysList.add(Calendar.THURSDAY);
165+
break;
166+
case "FR":
167+
byDaysList.add(Calendar.FRIDAY);
168+
break;
169+
case "SA":
170+
byDaysList.add(Calendar.SATURDAY);
171+
break;
172+
case "SU":
173+
byDaysList.add(Calendar.SUNDAY);
174+
break;
175+
default:
176+
throw new RuntimeException("bad day of week: " + day);
177+
}
178+
}
179+
return byDaysList;
180+
}
93181
}

app/src/main/java/org/gnucash/android/model/Recurrence.java

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import org.gnucash.android.R;
2323
import org.gnucash.android.app.GnuCashApplication;
2424
import org.gnucash.android.ui.util.RecurrenceParser;
25-
import org.joda.time.DateTime;
2625
import org.joda.time.Days;
2726
import org.joda.time.LocalDate;
2827
import org.joda.time.LocalDateTime;
@@ -32,8 +31,13 @@
3231
import org.joda.time.Years;
3332

3433
import java.sql.Timestamp;
34+
import java.text.DateFormat;
3535
import java.text.SimpleDateFormat;
36+
import java.util.ArrayList;
37+
import java.util.Calendar;
38+
import java.util.Collections;
3639
import java.util.Date;
40+
import java.util.List;
3741

3842
/**
3943
* Model for recurrences in the database
@@ -55,9 +59,9 @@ public class Recurrence extends BaseModel {
5559
private Timestamp mPeriodEnd;
5660

5761
/**
58-
* Describes which day on which to run the recurrence
62+
* Days of week on which to run the recurrence
5963
*/
60-
private String mByDay;
64+
private List<Integer> mByDays = Collections.emptyList();
6165

6266
public Recurrence(@NonNull PeriodType periodType){
6367
setPeriodType(periodType);
@@ -131,10 +135,9 @@ public String getRepeatString(){
131135
StringBuilder repeatBuilder = new StringBuilder(mPeriodType.getFrequencyRepeatString());
132136
Context context = GnuCashApplication.getAppContext();
133137

134-
String dayOfWeek = new SimpleDateFormat("EEEE", GnuCashApplication.getDefaultLocale())
135-
.format(new Date(mPeriodStart.getTime()));
136138
if (mPeriodType == PeriodType.WEEK) {
137-
repeatBuilder.append(" ").append(context.getString(R.string.repeat_on_weekday, dayOfWeek));
139+
repeatBuilder.append(" ").
140+
append(context.getString(R.string.repeat_on_weekday, getDaysOfWeekString()));
138141
}
139142

140143
if (mPeriodEnd != null){
@@ -144,7 +147,26 @@ public String getRepeatString(){
144147
return repeatBuilder.toString();
145148
}
146149

147-
/**
150+
/**
151+
* Returns a string with the days of the week set in the recurrence separated by commas.
152+
* @return string with the days of the week set in the recurrence separated by commas.
153+
*/
154+
private @NonNull String getDaysOfWeekString() {
155+
// XXX: mByDays should never be empty with PeriodType.WEEK, but we don't enforce it yet
156+
if (mByDays.isEmpty())
157+
return "";
158+
StringBuilder daysOfWeekString = new StringBuilder();
159+
Calendar calendar = Calendar.getInstance();
160+
DateFormat dayOfWeekFormatter =
161+
new SimpleDateFormat("EEEE", GnuCashApplication.getDefaultLocale());
162+
for (int day : mByDays) {
163+
calendar.set(Calendar.DAY_OF_WEEK, day);
164+
daysOfWeekString.append(dayOfWeekFormatter.format(calendar.getTime())).append(", ");
165+
}
166+
return daysOfWeekString.substring(0, daysOfWeekString.length()-2);
167+
}
168+
169+
/**
148170
* Creates an RFC 2445 string which describes this recurring event.
149171
* <p>See http://recurrance.sourceforge.net/</p>
150172
* <p>The output of this method is not meant for human consumption</p>
@@ -251,19 +273,27 @@ public String getTextOfCurrentPeriod(int periodNum){
251273
}
252274

253275
/**
254-
* Sets the string which determines on which day the recurrence will be run
255-
* @param byDay Byday string of recurrence rule (RFC 2445)
276+
* Return the days of week on which to run the recurrence.
277+
*
278+
* <p>Days are expressed as defined in {@link java.util.Calendar}.
279+
* For example, Calendar.MONDAY</p>
280+
*
281+
* @return list of days of week on which to run the recurrence.
256282
*/
257-
public void setByDay(String byDay){
258-
this.mByDay = byDay;
283+
public @NonNull List<Integer> getByDays(){
284+
return Collections.unmodifiableList(mByDays);
259285
}
260286

261287
/**
262-
* Return the byDay string of recurrence rule (RFC 2445)
263-
* @return String with by day specification
288+
* Sets the days on which to run the recurrence.
289+
*
290+
* <p>Days must be expressed as defined in {@link java.util.Calendar}.
291+
* For example, Calendar.MONDAY</p>
292+
*
293+
* @param byDays list of days of week on which to run the recurrence.
264294
*/
265-
public String getByDay(){
266-
return mByDay;
295+
public void setByDays(@NonNull List<Integer> byDays){
296+
mByDays = new ArrayList<>(byDays);
267297
}
268298

269299
/**

app/src/main/java/org/gnucash/android/ui/util/RecurrenceParser.java

Lines changed: 21 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.gnucash.android.ui.util;
1818

19+
import android.support.annotation.NonNull;
20+
import android.support.annotation.Nullable;
1921
import android.text.format.Time;
2022

2123
import com.codetroopers.betterpickers.recurrencepicker.EventRecurrence;
@@ -24,7 +26,10 @@
2426
import org.gnucash.android.model.Recurrence;
2527

2628
import java.sql.Timestamp;
29+
import java.util.ArrayList;
2730
import java.util.Calendar;
31+
import java.util.Collections;
32+
import java.util.List;
2833

2934
/**
3035
* Parses {@link EventRecurrence}s to generate
@@ -76,7 +81,7 @@ public static Recurrence parse(EventRecurrence eventRecurrence){
7681
periodType.setMultiplier(interval);
7782
Recurrence recurrence = new Recurrence(periodType);
7883
parseEndTime(eventRecurrence, recurrence);
79-
recurrence.setByDay(parseByDay(eventRecurrence.byday));
84+
recurrence.setByDays(parseByDay(eventRecurrence.byday));
8085
if (eventRecurrence.startDate != null)
8186
recurrence.setPeriodStart(new Timestamp(eventRecurrence.startDate.toMillis(false)));
8287

@@ -85,7 +90,7 @@ public static Recurrence parse(EventRecurrence eventRecurrence){
8590

8691
/**
8792
* Parses the end time from an EventRecurrence object and sets it to the <code>scheduledEvent</code>.
88-
* The end time is specified in the dialog either by number of occurences or a date.
93+
* The end time is specified in the dialog either by number of occurrences or a date.
8994
* @param eventRecurrence Event recurrence pattern obtained from dialog
9095
* @param recurrence Recurrence event to set the end period to
9196
*/
@@ -100,90 +105,24 @@ private static void parseEndTime(EventRecurrence eventRecurrence, Recurrence rec
100105
}
101106

102107
/**
103-
* Returns the date for the next day of the week
104-
* @param dow Day of the week (Calendar constants)
105-
* @return Calendar instance with the next day of the week
108+
* Parses an array of byDay values to return a list of days of week
109+
* constants from {@link Calendar}.
110+
*
111+
* <p>Currently only supports byDay values for weeks.</p>
112+
*
113+
* @param byDay Array of byDay values
114+
* @return list of days of week constants from Calendar.
106115
*/
107-
private static Calendar nextDayOfWeek(int dow) {
108-
Calendar date = Calendar.getInstance();
109-
int diff = dow - date.get(Calendar.DAY_OF_WEEK);
110-
if (!(diff > 0)) {
111-
diff += 7;
116+
private static @NonNull List<Integer> parseByDay(@Nullable int[] byDay) {
117+
if (byDay == null) {
118+
return Collections.emptyList();
112119
}
113-
date.add(Calendar.DAY_OF_MONTH, diff);
114-
return date;
115-
}
116120

117-
/**
118-
* Parses an array of byday values to return the string concatenation of days of the week.
119-
* <p>Currently only supports byDay values for weeks</p>
120-
* @param byday Array of byday values
121-
* @return String concat of days of the week or null if {@code byday} was empty
122-
*/
123-
private static String parseByDay(int[] byday){
124-
if (byday == null || byday.length == 0){
125-
return null;
126-
}
127-
//todo: parse for month and year as well, when our dialog supports those
128-
StringBuilder builder = new StringBuilder();
129-
for (int day : byday) {
130-
switch (day)
131-
{
132-
case EventRecurrence.SU:
133-
builder.append("SU");
134-
break;
135-
case EventRecurrence.MO:
136-
builder.append("MO");
137-
break;
138-
case EventRecurrence.TU:
139-
builder.append("TU");
140-
break;
141-
case EventRecurrence.WE:
142-
builder.append("WE");
143-
break;
144-
case EventRecurrence.TH:
145-
builder.append("TH");
146-
break;
147-
case EventRecurrence.FR:
148-
builder.append("FR");
149-
break;
150-
case EventRecurrence.SA:
151-
builder.append("SA");
152-
break;
153-
default:
154-
throw new RuntimeException("bad day of week: " + day);
155-
}
156-
builder.append(",");
121+
List<Integer> byDaysList = new ArrayList<>(byDay.length);
122+
for (int day : byDay) {
123+
byDaysList.add(EventRecurrence.day2CalendarDay(day));
157124
}
158-
builder.deleteCharAt(builder.length()-1);
159-
return builder.toString();
160-
}
161125

162-
/**
163-
* Converts one of the SU, MO, etc. constants to the Calendar.SUNDAY
164-
* constants. btw, I think we should switch to those here too, to
165-
* get rid of this function, if possible.
166-
*/
167-
public static int day2CalendarDay(int day)
168-
{
169-
switch (day)
170-
{
171-
case EventRecurrence.SU:
172-
return Calendar.SUNDAY;
173-
case EventRecurrence.MO:
174-
return Calendar.MONDAY;
175-
case EventRecurrence.TU:
176-
return Calendar.TUESDAY;
177-
case EventRecurrence.WE:
178-
return Calendar.WEDNESDAY;
179-
case EventRecurrence.TH:
180-
return Calendar.THURSDAY;
181-
case EventRecurrence.FR:
182-
return Calendar.FRIDAY;
183-
case EventRecurrence.SA:
184-
return Calendar.SATURDAY;
185-
default:
186-
throw new RuntimeException("bad day of week: " + day);
187-
}
126+
return byDaysList;
188127
}
189128
}

0 commit comments

Comments
 (0)