Add attendance (#58)

This commit is contained in:
Mikołaj Pich 2018-03-10 15:55:34 +01:00 committed by Rafał Borcz
parent 828e76821b
commit 3799fa910b
49 changed files with 2295 additions and 306 deletions

View File

@ -4,8 +4,12 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
import io.github.wulkanowy.api.SnP; import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.generic.Day; import io.github.wulkanowy.api.generic.Day;
@ -16,25 +20,34 @@ public class AttendanceTable {
private SnP snp; private SnP snp;
private String attendancePageUrl = "Frekwencja.mvc?data="; private final static String ATTENDANCE_PAGE_URL = "Frekwencja.mvc?data=";
public AttendanceTable(SnP snp) { public AttendanceTable(SnP snp) {
this.snp = snp; this.snp = snp;
} }
public Week<Day> getWeekTable() throws IOException { public Week<Day> getWeekTable() throws IOException, ParseException {
return getWeekTable(""); return getWeekTable("");
} }
public Week<Day> getWeekTable(String tick) throws IOException { public Week<Day> getWeekTable(String tick) throws IOException, ParseException {
Element table = snp.getSnPPageDocument(attendancePageUrl + tick) Element table = snp.getSnPPageDocument(ATTENDANCE_PAGE_URL + tick)
.select(".mainContainer .presentData").first(); .select(".mainContainer .presentData").first();
Elements headerCells = table.select("thead th"); Elements headerCells = table.select("thead th");
List<Day> days = new ArrayList<>(); List<Day> days = new ArrayList<>();
for (int i = 1; i < headerCells.size(); i++) { for (int i = 1; i < headerCells.size(); i++) {
days.add(new Day().setDate(headerCells.get(i).html().split("<br>")[1])); String[] dayHeaderCell = headerCells.get(i).html().split("<br>");
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT);
Date d = sdf.parse(dayHeaderCell[1].trim());
sdf.applyPattern("yyyy-MM-dd");
Day day = new Day();
day.setDayName(dayHeaderCell[0]);
day.setDate(sdf.format(d));
days.add(day);
} }
Elements hoursInDays = table.select("tbody tr"); Elements hoursInDays = table.select("tbody tr");
@ -46,26 +59,29 @@ public class AttendanceTable {
// fill hours in day // fill hours in day
int size = hours.size(); int size = hours.size();
for (int i = 1; i < size; i++) { for (int i = 1; i < size; i++) {
days.get(i - 1).setLesson(getNewLesson(hours.get(i))); Lesson lesson = new Lesson();
} lesson.setDate(days.get(i - 1).getDate());
} lesson.setNumber(hours.get(0).text());
String[] dayDescription = headerCells.get(1).html().split("<br>"); addLessonDetails(lesson, hours.get(i));
days.get(i - 1).setLesson(lesson);
}
}
return new Week<Day>() return new Week<Day>()
.setStartDayDate(dayDescription[1]) .setStartDayDate(days.get(0).getDate())
.setDays(days); .setDays(days);
} }
private Lesson getNewLesson(Element cell) { private void addLessonDetails(Lesson lesson, Element cell) {
Lesson lesson = new Lesson();
lesson.setSubject(cell.select("span").text()); lesson.setSubject(cell.select("span").text());
if (LessonTypes.CLASS_NOT_EXIST.equals(cell.attr("class"))) { if (LessonTypes.CLASS_NOT_EXIST.equals(cell.attr("class"))) {
lesson.setNotExist(true); lesson.setNotExist(true);
lesson.setEmpty(true); lesson.setEmpty(true);
return lesson; return;
} }
switch (cell.select("div").attr("class")) { switch (cell.select("div").attr("class")) {
@ -95,7 +111,5 @@ public class AttendanceTable {
lesson.setEmpty(true); lesson.setEmpty(true);
break; break;
} }
return lesson;
} }
} }

View File

@ -20,8 +20,8 @@ public class AttendanceTableTest extends StudentAndParentTestCase {
@Test @Test
public void getWeekStartByDate() throws Exception { public void getWeekStartByDate() throws Exception {
Assert.assertEquals("31.08.2015", excellent.getWeekTable().getStartDayDate()); Assert.assertEquals("2015-08-31", excellent.getWeekTable().getStartDayDate());
Assert.assertEquals("05.09.2016", full.getWeekTable().getStartDayDate()); Assert.assertEquals("2016-09-05", full.getWeekTable().getStartDayDate());
} }
@Test @Test
@ -38,13 +38,13 @@ public class AttendanceTableTest extends StudentAndParentTestCase {
@Test @Test
public void getDayDate() throws Exception { public void getDayDate() throws Exception {
Assert.assertEquals("31.08.2015", excellent.getWeekTable().getDay(0).getDate()); Assert.assertEquals("2015-08-31", excellent.getWeekTable().getDay(0).getDate());
Assert.assertEquals("02.09.2015", excellent.getWeekTable().getDay(2).getDate()); Assert.assertEquals("2015-09-02", excellent.getWeekTable().getDay(2).getDate());
Assert.assertEquals("04.09.2015", excellent.getWeekTable().getDay(4).getDate()); Assert.assertEquals("2015-09-04", excellent.getWeekTable().getDay(4).getDate());
Assert.assertEquals("05.09.2016", full.getWeekTable().getDay(0).getDate()); Assert.assertEquals("2016-09-05", full.getWeekTable().getDay(0).getDate());
Assert.assertEquals("07.09.2016", full.getWeekTable().getDay(2).getDate()); Assert.assertEquals("2016-09-07", full.getWeekTable().getDay(2).getDate());
Assert.assertEquals("09.09.2016", full.getWeekTable().getDay(4).getDate()); Assert.assertEquals("2016-09-09", full.getWeekTable().getDay(4).getDate());
} }
@Test @Test

View File

@ -62,7 +62,7 @@ android {
} }
greendao { greendao {
schemaVersion 20 schemaVersion 22
generateTests = true generateTests = true
} }

View File

@ -0,0 +1,28 @@
package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.test.AbstractDaoTestLongPk;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLessonDao;
public class AttendanceLessonTest extends AbstractDaoTestLongPk<AttendanceLessonDao, AttendanceLesson> {
public AttendanceLessonTest() {
super(AttendanceLessonDao.class);
}
@Override
protected AttendanceLesson createEntity(Long key) {
AttendanceLesson entity = new AttendanceLesson();
entity.setId(key);
entity.setIsPresence(false);
entity.setIsAbsenceUnexcused(false);
entity.setIsAbsenceExcused(false);
entity.setIsUnexcusedLateness(false);
entity.setIsAbsenceForSchoolReasons(false);
entity.setIsExcusedLateness(false);
entity.setIsExemption(false);
return entity;
}
}

View File

@ -2,15 +2,18 @@ package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.test.AbstractDaoTestLongPk; import org.greenrobot.greendao.test.AbstractDaoTestLongPk;
public class LessonTest extends AbstractDaoTestLongPk<LessonDao, Lesson> { import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao;
public LessonTest() { public class TimetableLessonTest extends AbstractDaoTestLongPk<TimetableLessonDao, TimetableLesson> {
super(LessonDao.class);
public TimetableLessonTest() {
super(TimetableLessonDao.class);
} }
@Override @Override
protected Lesson createEntity(Long key) { protected TimetableLesson createEntity(Long key) {
Lesson entity = new Lesson(); TimetableLesson entity = new TimetableLesson();
entity.setId(key); entity.setId(key);
entity.setIsEmpty(false); entity.setIsEmpty(false);
entity.setIsDivisionIntoGroups(false); entity.setIsDivisionIntoGroups(false);

View File

@ -1,6 +1,5 @@
package io.github.wulkanowy.data; package io.github.wulkanowy.data;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException; import java.text.ParseException;
import java.util.List; import java.util.List;
@ -13,6 +12,7 @@ import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.api.login.NotLoggedInErrorException; import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException; import io.github.wulkanowy.api.login.VulcanOfflineException;
import io.github.wulkanowy.data.db.dao.entities.Account; import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.data.db.dao.entities.DaoSession; import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Grade; import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.GradeDao; import io.github.wulkanowy.data.db.dao.entities.GradeDao;
@ -21,6 +21,7 @@ import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.data.db.resources.ResourcesContract; import io.github.wulkanowy.data.db.resources.ResourcesContract;
import io.github.wulkanowy.data.db.shared.SharedPrefContract; import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.data.sync.SyncContract; import io.github.wulkanowy.data.sync.SyncContract;
import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract;
import io.github.wulkanowy.data.sync.login.LoginSyncContract; import io.github.wulkanowy.data.sync.login.LoginSyncContract;
import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract; import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract;
import io.github.wulkanowy.di.annotations.SyncGrades; import io.github.wulkanowy.di.annotations.SyncGrades;
@ -38,6 +39,8 @@ public class Repository implements RepositoryContract {
private final LoginSyncContract loginSync; private final LoginSyncContract loginSync;
private final AttendanceSyncContract attendanceSync;
private final TimetableSyncContract timetableSync; private final TimetableSyncContract timetableSync;
private final SyncContract gradeSync; private final SyncContract gradeSync;
@ -49,6 +52,7 @@ public class Repository implements RepositoryContract {
ResourcesContract resources, ResourcesContract resources,
DaoSession daoSession, DaoSession daoSession,
LoginSyncContract loginSync, LoginSyncContract loginSync,
AttendanceSyncContract attendanceSync,
TimetableSyncContract timetableSync, TimetableSyncContract timetableSync,
@SyncGrades SyncContract gradeSync, @SyncGrades SyncContract gradeSync,
@SyncSubjects SyncContract subjectSync) { @SyncSubjects SyncContract subjectSync) {
@ -56,6 +60,7 @@ public class Repository implements RepositoryContract {
this.resources = resources; this.resources = resources;
this.daoSession = daoSession; this.daoSession = daoSession;
this.loginSync = loginSync; this.loginSync = loginSync;
this.attendanceSync = attendanceSync;
this.timetableSync = timetableSync; this.timetableSync = timetableSync;
this.gradeSync = gradeSync; this.gradeSync = gradeSync;
this.subjectSync = subjectSync; this.subjectSync = subjectSync;
@ -81,6 +86,11 @@ public class Repository implements RepositoryContract {
return resources.getErrorLoginMessage(e); return resources.getErrorLoginMessage(e);
} }
@Override
public String getAttendanceLessonDescription(AttendanceLesson lesson) {
return resources.getAttendanceLessonDescription(lesson);
}
@Override @Override
public void loginUser(String email, String password, String symbol) public void loginUser(String email, String password, String symbol)
throws NotLoggedInErrorException, AccountPermissionException, IOException, throws NotLoggedInErrorException, AccountPermissionException, IOException,
@ -104,6 +114,16 @@ public class Repository implements RepositoryContract {
subjectSync.sync(); subjectSync.sync();
} }
@Override
public void syncAttendance() throws NotLoggedInErrorException, ParseException, IOException {
attendanceSync.syncAttendance();
}
@Override
public void syncAttendance(String date) throws NotLoggedInErrorException, ParseException, IOException {
attendanceSync.syncAttendance(date);
}
@Override @Override
public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException { public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException {
timetableSync.syncTimetable(); timetableSync.syncTimetable();
@ -118,6 +138,7 @@ public class Repository implements RepositoryContract {
public void syncAll() throws NotLoggedInErrorException, IOException, ParseException { public void syncAll() throws NotLoggedInErrorException, IOException, ParseException {
syncSubjects(); syncSubjects();
syncGrades(); syncGrades();
syncAttendance();
syncTimetable(); syncTimetable();
} }

View File

@ -11,12 +11,13 @@ import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.Grade; import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.Week; import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.resources.ResourcesContract; import io.github.wulkanowy.data.db.resources.ResourcesContract;
import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract;
import io.github.wulkanowy.data.sync.login.LoginSyncContract; import io.github.wulkanowy.data.sync.login.LoginSyncContract;
import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract; import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract;
@Singleton @Singleton
public interface RepositoryContract extends ResourcesContract, LoginSyncContract, public interface RepositoryContract extends ResourcesContract, LoginSyncContract,
TimetableSyncContract { AttendanceSyncContract, TimetableSyncContract {
long getCurrentUserId(); long getCurrentUserId();

View File

@ -0,0 +1,254 @@
package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.DaoException;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Index;
import org.greenrobot.greendao.annotation.Property;
import org.greenrobot.greendao.annotation.Transient;
import java.io.Serializable;
@Entity(
nameInDb = "AttendanceLessons",
active = true,
indexes = {@Index(value = "dayId,date,number", unique = true)}
)
public class AttendanceLesson implements Serializable {
@Id(autoincrement = true)
private Long id;
@Property(nameInDb = "DAY_ID")
private Long dayId;
@Property(nameInDb = "DATE")
private String date = "";
@Property(nameInDb = "NUMBER_OF_LESSON")
private int number = 0;
@Property(nameInDb = "SUBJECT_NAME")
private String subject = "";
@Property(nameInDb = "IS_PRESENCE")
private boolean isPresence = false;
@Property(nameInDb = "IS_ABSENCE_UNEXCUSED")
private boolean isAbsenceUnexcused = false;
@Property(nameInDb = "IS_ABSENCE_EXCUSED")
private boolean isAbsenceExcused = false;
@Property(nameInDb = "IS_UNEXCUSED_LATENESS")
private boolean isUnexcusedLateness = false;
@Property(nameInDb = "IS_ABSENCE_FOR_SCHOOL_REASONS")
private boolean isAbsenceForSchoolReasons = false;
@Property(nameInDb = "IS_EXCUSED_LATENESS")
private boolean isExcusedLateness = false;
@Property(nameInDb = "IS_EXEMPTION")
private boolean isExemption = false;
@Transient
private String description = "";
private static final long serialVersionUID = 42L;
/**
* Used to resolve relations
*/
@Generated(hash = 2040040024)
private transient DaoSession daoSession;
/**
* Used for active entity operations.
*/
@Generated(hash = 1936953859)
private transient AttendanceLessonDao myDao;
@Generated(hash = 1428129046)
public AttendanceLesson(Long id, Long dayId, String date, int number,
String subject, boolean isPresence, boolean isAbsenceUnexcused,
boolean isAbsenceExcused, boolean isUnexcusedLateness,
boolean isAbsenceForSchoolReasons, boolean isExcusedLateness,
boolean isExemption) {
this.id = id;
this.dayId = dayId;
this.date = date;
this.number = number;
this.subject = subject;
this.isPresence = isPresence;
this.isAbsenceUnexcused = isAbsenceUnexcused;
this.isAbsenceExcused = isAbsenceExcused;
this.isUnexcusedLateness = isUnexcusedLateness;
this.isAbsenceForSchoolReasons = isAbsenceForSchoolReasons;
this.isExcusedLateness = isExcusedLateness;
this.isExemption = isExemption;
}
@Generated(hash = 921806575)
public AttendanceLesson() {
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public Long getDayId() {
return this.dayId;
}
public void setDayId(Long dayId) {
this.dayId = dayId;
}
public String getDate() {
return this.date;
}
public AttendanceLesson setDate(String date) {
this.date = date;
return this;
}
public int getNumber() {
return this.number;
}
public AttendanceLesson setNumber(int number) {
this.number = number;
return this;
}
public String getSubject() {
return this.subject;
}
public AttendanceLesson setSubject(String subject) {
this.subject = subject;
return this;
}
public boolean getIsPresence() {
return this.isPresence;
}
public AttendanceLesson setIsPresence(boolean isPresence) {
this.isPresence = isPresence;
return this;
}
public boolean getIsAbsenceUnexcused() {
return this.isAbsenceUnexcused;
}
public AttendanceLesson setIsAbsenceUnexcused(boolean isAbsenceUnexcused) {
this.isAbsenceUnexcused = isAbsenceUnexcused;
return this;
}
public boolean getIsAbsenceExcused() {
return this.isAbsenceExcused;
}
public AttendanceLesson setIsAbsenceExcused(boolean isAbsenceExcused) {
this.isAbsenceExcused = isAbsenceExcused;
return this;
}
public boolean getIsUnexcusedLateness() {
return this.isUnexcusedLateness;
}
public AttendanceLesson setIsUnexcusedLateness(boolean isUnexcusedLateness) {
this.isUnexcusedLateness = isUnexcusedLateness;
return this;
}
public boolean getIsAbsenceForSchoolReasons() {
return this.isAbsenceForSchoolReasons;
}
public AttendanceLesson setIsAbsenceForSchoolReasons(boolean isAbsenceForSchoolReasons) {
this.isAbsenceForSchoolReasons = isAbsenceForSchoolReasons;
return this;
}
public boolean getIsExcusedLateness() {
return this.isExcusedLateness;
}
public AttendanceLesson setIsExcusedLateness(boolean isExcusedLateness) {
this.isExcusedLateness = isExcusedLateness;
return this;
}
public boolean getIsExemption() {
return this.isExemption;
}
public AttendanceLesson setIsExemption(boolean isExemption) {
this.isExemption = isExemption;
return this;
}
public String getDescription() {
return description;
}
public AttendanceLesson setDescription(String description) {
this.description = description;
return this;
}
/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 128553479)
public void delete() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.delete(this);
}
/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 1942392019)
public void refresh() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.refresh(this);
}
/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 713229351)
public void update() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.update(this);
}
/** called by internal mechanisms, do not call yourself. */
@Generated(hash = 1157101112)
public void __setDaoSession(DaoSession daoSession) {
this.daoSession = daoSession;
myDao = daoSession != null ? daoSession.getAttendanceLessonDao() : null;
}
}

View File

@ -5,6 +5,7 @@ import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated; import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id; import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Index; import org.greenrobot.greendao.annotation.Index;
import org.greenrobot.greendao.annotation.OrderBy;
import org.greenrobot.greendao.annotation.Property; import org.greenrobot.greendao.annotation.Property;
import org.greenrobot.greendao.annotation.ToMany; import org.greenrobot.greendao.annotation.ToMany;
@ -39,7 +40,11 @@ public class Day {
private String freeDayName = ""; private String freeDayName = "";
@ToMany(referencedJoinProperty = "dayId") @ToMany(referencedJoinProperty = "dayId")
private List<Lesson> lessons; private List<TimetableLesson> timetableLessons;
@ToMany(referencedJoinProperty = "dayId")
@OrderBy("number ASC")
private List<AttendanceLesson> attendanceLessons;
/** /**
* Used to resolve relations * Used to resolve relations
@ -113,12 +118,12 @@ public class Day {
return this; return this;
} }
public boolean isFreeDay() { public boolean getIsFreeDay() {
return isFreeDay; return this.isFreeDay;
} }
public Day setFreeDay(boolean freeDay) { public Day setIsFreeDay(boolean isFreeDay) {
isFreeDay = freeDay; this.isFreeDay = isFreeDay;
return this; return this;
} }
@ -135,30 +140,58 @@ public class Day {
* To-many relationship, resolved on first access (and after reset). * To-many relationship, resolved on first access (and after reset).
* Changes to to-many relations are not persisted, make changes to the target entity. * Changes to to-many relations are not persisted, make changes to the target entity.
*/ */
@Generated(hash = 1552857303) @Generated(hash = 218588195)
public List<Lesson> getLessons() { public List<TimetableLesson> getTimetableLessons() {
if (lessons == null) { if (timetableLessons == null) {
final DaoSession daoSession = this.daoSession; final DaoSession daoSession = this.daoSession;
if (daoSession == null) { if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context"); throw new DaoException("Entity is detached from DAO context");
} }
LessonDao targetDao = daoSession.getLessonDao(); TimetableLessonDao targetDao = daoSession.getTimetableLessonDao();
List<Lesson> lessonsNew = targetDao._queryDay_Lessons(id); List<TimetableLesson> timetableLessonsNew = targetDao
._queryDay_TimetableLessons(id);
synchronized (this) { synchronized (this) {
if (lessons == null) { if (timetableLessons == null) {
lessons = lessonsNew; timetableLessons = timetableLessonsNew;
} }
} }
} }
return lessons; return timetableLessons;
}
/** Resets a to-many relationship, making the next get call to query for a fresh result. */
@Generated(hash = 1687683740)
public synchronized void resetTimetableLessons() {
timetableLessons = null;
} }
/** /**
* Resets a to-many relationship, making the next get call to query for a fresh result. * To-many relationship, resolved on first access (and after reset).
* Changes to to-many relations are not persisted, make changes to the target entity.
*/ */
@Generated(hash = 1769801440) @Generated(hash = 1166820581)
public synchronized void resetLessons() { public List<AttendanceLesson> getAttendanceLessons() {
lessons = null; if (attendanceLessons == null) {
final DaoSession daoSession = this.daoSession;
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
AttendanceLessonDao targetDao = daoSession.getAttendanceLessonDao();
List<AttendanceLesson> attendanceLessonsNew = targetDao
._queryDay_AttendanceLessons(id);
synchronized (this) {
if (attendanceLessons == null) {
attendanceLessons = attendanceLessonsNew;
}
}
}
return attendanceLessons;
}
/** Resets a to-many relationship, making the next get call to query for a fresh result. */
@Generated(hash = 1343075564)
public synchronized void resetAttendanceLessons() {
attendanceLessons = null;
} }
/** /**
@ -197,14 +230,6 @@ public class Day {
myDao.update(this); myDao.update(this);
} }
public boolean getIsFreeDay() {
return this.isFreeDay;
}
public void setIsFreeDay(boolean isFreeDay) {
this.isFreeDay = isFreeDay;
}
/** called by internal mechanisms, do not call yourself. */ /** called by internal mechanisms, do not call yourself. */
@Generated(hash = 1409317752) @Generated(hash = 1409317752)
public void __setDaoSession(DaoSession daoSession) { public void __setDaoSession(DaoSession daoSession) {

View File

@ -10,11 +10,11 @@ import org.greenrobot.greendao.annotation.Property;
import java.io.Serializable; import java.io.Serializable;
@Entity( @Entity(
nameInDb = "Lessons", nameInDb = "TimetableLessons",
active = true, active = true,
indexes = {@Index(value = "dayId,date,startTime,endTime", unique = true)} indexes = {@Index(value = "dayId,date,startTime,endTime", unique = true)}
) )
public class Lesson implements Serializable { public class TimetableLesson implements Serializable {
@Id(autoincrement = true) @Id(autoincrement = true)
private Long id; private Long id;
@ -78,13 +78,14 @@ public class Lesson implements Serializable {
/** /**
* Used for active entity operations. * Used for active entity operations.
*/ */
@Generated(hash = 610143130) @Generated(hash = 1119360138)
private transient LessonDao myDao; private transient TimetableLessonDao myDao;
@Generated(hash = 140778287) @Generated(hash = 627457324)
public Lesson(Long id, Long dayId, String number, String subject, String teacher, String room, public TimetableLesson(Long id, Long dayId, String number, String subject,
String description, String groupName, String startTime, String endTime, String date, String teacher, String room, String description, String groupName,
boolean isEmpty, boolean isDivisionIntoGroups, boolean isPlanning, boolean isRealized, String startTime, String endTime, String date, boolean isEmpty,
boolean isDivisionIntoGroups, boolean isPlanning, boolean isRealized,
boolean isMovedOrCanceled, boolean isNewMovedInOrChanged) { boolean isMovedOrCanceled, boolean isNewMovedInOrChanged) {
this.id = id; this.id = id;
this.dayId = dayId; this.dayId = dayId;
@ -105,8 +106,8 @@ public class Lesson implements Serializable {
this.isNewMovedInOrChanged = isNewMovedInOrChanged; this.isNewMovedInOrChanged = isNewMovedInOrChanged;
} }
@Generated(hash = 1669664117) @Generated(hash = 1878030142)
public Lesson() { public TimetableLesson() {
} }
public Long getId() { public Long getId() {
@ -126,186 +127,138 @@ public class Lesson implements Serializable {
} }
public String getNumber() { public String getNumber() {
return number; return this.number;
} }
public Lesson setNumber(String number) { public TimetableLesson setNumber(String number) {
this.number = number; this.number = number;
return this; return this;
} }
public String getSubject() { public String getSubject() {
return subject; return this.subject;
} }
public Lesson setSubject(String subject) { public TimetableLesson setSubject(String subject) {
this.subject = subject; this.subject = subject;
return this; return this;
} }
public String getTeacher() { public String getTeacher() {
return teacher; return this.teacher;
} }
public Lesson setTeacher(String teacher) { public TimetableLesson setTeacher(String teacher) {
this.teacher = teacher; this.teacher = teacher;
return this; return this;
} }
public String getRoom() { public String getRoom() {
return room; return this.room;
} }
public Lesson setRoom(String room) { public TimetableLesson setRoom(String room) {
this.room = room; this.room = room;
return this; return this;
} }
public String getDescription() { public String getDescription() {
return description; return this.description;
} }
public Lesson setDescription(String description) { public TimetableLesson setDescription(String description) {
this.description = description; this.description = description;
return this; return this;
} }
public String getGroupName() { public String getGroupName() {
return groupName; return this.groupName;
} }
public Lesson setGroupName(String groupName) { public TimetableLesson setGroupName(String groupName) {
this.groupName = groupName; this.groupName = groupName;
return this; return this;
} }
public String getStartTime() { public String getStartTime() {
return startTime; return this.startTime;
} }
public Lesson setStartTime(String startTime) { public TimetableLesson setStartTime(String startTime) {
this.startTime = startTime; this.startTime = startTime;
return this; return this;
} }
public String getEndTime() { public String getEndTime() {
return endTime; return this.endTime;
} }
public Lesson setEndTime(String endTime) { public TimetableLesson setEndTime(String endTime) {
this.endTime = endTime; this.endTime = endTime;
return this; return this;
} }
public String getDate() { public String getDate() {
return date; return this.date;
} }
public Lesson setDate(String date) { public TimetableLesson setDate(String date) {
this.date = date; this.date = date;
return this; return this;
} }
public boolean isEmpty() {
return isEmpty;
}
public Lesson setEmpty(boolean empty) {
isEmpty = empty;
return this;
}
public boolean isDivisionIntoGroups() {
return isDivisionIntoGroups;
}
public Lesson setDivisionIntoGroups(boolean divisionIntoGroups) {
isDivisionIntoGroups = divisionIntoGroups;
return this;
}
public boolean isPlanning() {
return isPlanning;
}
public Lesson setPlanning(boolean planning) {
isPlanning = planning;
return this;
}
public boolean isRealized() {
return isRealized;
}
public Lesson setRealized(boolean realized) {
isRealized = realized;
return this;
}
public boolean isMovedOrCanceled() {
return isMovedOrCanceled;
}
public Lesson setMovedOrCanceled(boolean movedOrCanceled) {
isMovedOrCanceled = movedOrCanceled;
return this;
}
public boolean isNewMovedInOrChanged() {
return isNewMovedInOrChanged;
}
public Lesson setNewMovedInOrChanged(boolean newMovedInOrChanged) {
isNewMovedInOrChanged = newMovedInOrChanged;
return this;
}
public boolean getIsEmpty() { public boolean getIsEmpty() {
return this.isEmpty; return this.isEmpty;
} }
public void setIsEmpty(boolean isEmpty) { public TimetableLesson setEmpty(boolean isEmpty) {
this.isEmpty = isEmpty; this.isEmpty = isEmpty;
return this;
} }
public boolean getIsDivisionIntoGroups() { public boolean getIsDivisionIntoGroups() {
return this.isDivisionIntoGroups; return this.isDivisionIntoGroups;
} }
public void setIsDivisionIntoGroups(boolean isDivisionIntoGroups) { public TimetableLesson setDivisionIntoGroups(boolean isDivisionIntoGroups) {
this.isDivisionIntoGroups = isDivisionIntoGroups; this.isDivisionIntoGroups = isDivisionIntoGroups;
return this;
} }
public boolean getIsPlanning() { public boolean getIsPlanning() {
return this.isPlanning; return this.isPlanning;
} }
public void setIsPlanning(boolean isPlanning) { public TimetableLesson setPlanning(boolean isPlanning) {
this.isPlanning = isPlanning; this.isPlanning = isPlanning;
return this;
} }
public boolean getIsRealized() { public boolean getIsRealized() {
return this.isRealized; return this.isRealized;
} }
public void setIsRealized(boolean isRealized) { public TimetableLesson setRealized(boolean isRealized) {
this.isRealized = isRealized; this.isRealized = isRealized;
return this;
} }
public boolean getIsMovedOrCanceled() { public boolean getIsMovedOrCanceled() {
return this.isMovedOrCanceled; return this.isMovedOrCanceled;
} }
public void setIsMovedOrCanceled(boolean isMovedOrCanceled) { public TimetableLesson setMovedOrCanceled(boolean isMovedOrCanceled) {
this.isMovedOrCanceled = isMovedOrCanceled; this.isMovedOrCanceled = isMovedOrCanceled;
return this;
} }
public boolean getIsNewMovedInOrChanged() { public boolean getIsNewMovedInOrChanged() {
return this.isNewMovedInOrChanged; return this.isNewMovedInOrChanged;
} }
public void setIsNewMovedInOrChanged(boolean isNewMovedInOrChanged) { public TimetableLesson setNewMovedInOrChanged(boolean isNewMovedInOrChanged) {
this.isNewMovedInOrChanged = isNewMovedInOrChanged; this.isNewMovedInOrChanged = isNewMovedInOrChanged;
return this;
} }
/** /**
@ -344,10 +297,34 @@ public class Lesson implements Serializable {
myDao.update(this); myDao.update(this);
} }
public void setIsEmpty(boolean isEmpty) {
this.isEmpty = isEmpty;
}
public void setIsDivisionIntoGroups(boolean isDivisionIntoGroups) {
this.isDivisionIntoGroups = isDivisionIntoGroups;
}
public void setIsPlanning(boolean isPlanning) {
this.isPlanning = isPlanning;
}
public void setIsRealized(boolean isRealized) {
this.isRealized = isRealized;
}
public void setIsMovedOrCanceled(boolean isMovedOrCanceled) {
this.isMovedOrCanceled = isMovedOrCanceled;
}
public void setIsNewMovedInOrChanged(boolean isNewMovedInOrChanged) {
this.isNewMovedInOrChanged = isNewMovedInOrChanged;
}
/** called by internal mechanisms, do not call yourself. */ /** called by internal mechanisms, do not call yourself. */
@Generated(hash = 2078826279) @Generated(hash = 1885258429)
public void __setDaoSession(DaoSession daoSession) { public void __setDaoSession(DaoSession daoSession) {
this.daoSession = daoSession; this.daoSession = daoSession;
myDao = daoSession != null ? daoSession.getLessonDao() : null; myDao = daoSession != null ? daoSession.getTimetableLessonDao() : null;
} }
} }

View File

@ -26,6 +26,12 @@ public class Week {
@Property(nameInDb = "START_DATE") @Property(nameInDb = "START_DATE")
private String startDayDate = ""; private String startDayDate = "";
@Property(nameInDb = "IS_ATTENDANCE_SYNCED")
private boolean isAttendanceSynced = false;
@Property(nameInDb = "IS_TIMETABLE_SYNCED")
private boolean isTimetableSynced = false;
@ToMany(referencedJoinProperty = "weekId") @ToMany(referencedJoinProperty = "weekId")
private List<Day> dayList; private List<Day> dayList;
@ -37,11 +43,14 @@ public class Week {
@Generated(hash = 1019310398) @Generated(hash = 1019310398)
private transient WeekDao myDao; private transient WeekDao myDao;
@Generated(hash = 36829814) @Generated(hash = 1745118398)
public Week(Long id, Long userId, String startDayDate) { public Week(Long id, Long userId, String startDayDate, boolean isAttendanceSynced,
boolean isTimetableSynced) {
this.id = id; this.id = id;
this.userId = userId; this.userId = userId;
this.startDayDate = startDayDate; this.startDayDate = startDayDate;
this.isAttendanceSynced = isAttendanceSynced;
this.isTimetableSynced = isTimetableSynced;
} }
@Generated(hash = 2135529658) @Generated(hash = 2135529658)
@ -75,6 +84,22 @@ public class Week {
return this; return this;
} }
public boolean getIsAttendanceSynced() {
return this.isAttendanceSynced;
}
public void setIsAttendanceSynced(boolean isAttendanceSynced) {
this.isAttendanceSynced = isAttendanceSynced;
}
public boolean getIsTimetableSynced() {
return this.isTimetableSynced;
}
public void setIsTimetableSynced(boolean isTimetableSynced) {
this.isTimetableSynced = isTimetableSynced;
}
/** /**
* To-many relationship, resolved on first access (and after reset). * To-many relationship, resolved on first access (and after reset).
* Changes to to-many relations are not persisted, make changes to the target entity. * Changes to to-many relations are not persisted, make changes to the target entity.

View File

@ -13,6 +13,7 @@ import javax.inject.Singleton;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.api.login.NotLoggedInErrorException; import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException; import io.github.wulkanowy.api.login.VulcanOfflineException;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.di.annotations.ApplicationContext; import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.utils.AppConstant; import io.github.wulkanowy.utils.AppConstant;
import io.github.wulkanowy.utils.LogUtils; import io.github.wulkanowy.utils.LogUtils;
@ -56,4 +57,35 @@ public class AppResources implements ResourcesContract {
return exception.getMessage(); return exception.getMessage();
} }
} }
@Override
public String getAttendanceLessonDescription(AttendanceLesson lesson) {
int id = R.string.attendance_present;
if (lesson.getIsAbsenceForSchoolReasons()) {
id = R.string.attendance_absence_for_school_reasons;
}
if (lesson.getIsAbsenceExcused()) {
id = R.string.attendance_absence_excused;
}
if (lesson.getIsAbsenceUnexcused()) {
id = R.string.attendance_absence_unexcused;
}
if (lesson.getIsExemption()) {
id = R.string.attendance_exemption;
}
if (lesson.getIsExcusedLateness()) {
id = R.string.attendance_excused_lateness;
}
if (lesson.getIsUnexcusedLateness()) {
id = R.string.attendance_unexcused_lateness;
}
return resources.getString(id);
}
} }

View File

@ -1,5 +1,7 @@
package io.github.wulkanowy.data.db.resources; package io.github.wulkanowy.data.db.resources;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
public interface ResourcesContract { public interface ResourcesContract {
String[] getSymbolsKeysArray(); String[] getSymbolsKeysArray();
@ -7,4 +9,6 @@ public interface ResourcesContract {
String[] getSymbolsValuesArray(); String[] getSymbolsValuesArray();
String getErrorLoginMessage(Exception e); String getErrorLoginMessage(Exception e);
String getAttendanceLessonDescription(AttendanceLesson lesson);
} }

View File

@ -0,0 +1,156 @@
package io.github.wulkanowy.data.sync.attendance;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.generic.Lesson;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLessonDao;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Day;
import io.github.wulkanowy.data.db.dao.entities.DayDao;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.TimeUtils;
@Singleton
public class AttendanceSync implements AttendanceSyncContract {
private final DaoSession daoSession;
private final SharedPrefContract sharedPref;
private final Vulcan vulcan;
private long userId;
@Inject
AttendanceSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) {
this.daoSession = daoSession;
this.sharedPref = sharedPref;
this.vulcan = vulcan;
}
@Override
public void syncAttendance() throws IOException, NotLoggedInErrorException, ParseException {
syncAttendance(null);
}
@Override
public void syncAttendance(String date) throws IOException, NotLoggedInErrorException, ParseException {
this.userId = sharedPref.getCurrentUserId();
io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> weekApi = getWeekFromApi(getNormalizedDate(date));
Week weekDb = getWeekFromDb(weekApi.getStartDayDate());
long weekId = updateWeekInDb(weekDb, weekApi);
List<AttendanceLesson> lessonList = updateDays(weekApi.getDays(), weekId);
daoSession.getAttendanceLessonDao().saveInTx(lessonList);
LogUtils.debug("Synchronization lessons (amount = " + lessonList.size() + ")");
}
private String getNormalizedDate(String date) throws ParseException {
return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : "";
}
private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> getWeekFromApi(String date)
throws IOException, NotLoggedInErrorException, ParseException {
return vulcan.getAttendanceTable().getWeekTable(date);
}
private Week getWeekFromDb(String date) {
return daoSession.getWeekDao()
.queryBuilder()
.where(WeekDao.Properties.UserId.eq(userId), WeekDao.Properties.StartDayDate.eq(date))
.unique();
}
private Long updateWeekInDb(Week dbWeekEntity, io.github.wulkanowy.api.generic.Week fromApi) {
if (dbWeekEntity != null) {
dbWeekEntity.setIsAttendanceSynced(true);
dbWeekEntity.update();
return dbWeekEntity.getId();
}
Week apiWeekEntity = DataObjectConverter.weekToWeekEntity(fromApi).setUserId(userId);
apiWeekEntity.setIsAttendanceSynced(true);
return daoSession.getWeekDao().insert(apiWeekEntity);
}
private List<AttendanceLesson> updateDays(List<io.github.wulkanowy.api.generic.Day> dayListFromApi, long weekId) {
List<AttendanceLesson> updatedLessonList = new ArrayList<>();
for (io.github.wulkanowy.api.generic.Day dayFromApi : dayListFromApi) {
Day dbDayEntity = getDayFromDb(dayFromApi.getDate());
Day apiDayEntity = DataObjectConverter.dayToDayEntity(dayFromApi);
long dayId = updateDay(dbDayEntity, apiDayEntity, weekId);
updateLessons(dayFromApi.getLessons(), updatedLessonList, dayId);
}
return updatedLessonList;
}
private Day getDayFromDb(String date) {
return daoSession.getDayDao()
.queryBuilder()
.where(DayDao.Properties.UserId.eq(userId), DayDao.Properties.Date.eq(date))
.unique();
}
private long updateDay(Day dbDayEntity, Day apiDayEntity, long weekId) {
if (null != dbDayEntity) {
return dbDayEntity.getId();
}
apiDayEntity.setUserId(userId);
apiDayEntity.setWeekId(weekId);
return daoSession.getDayDao().insert(apiDayEntity);
}
private void updateLessons(List<Lesson> lessons, List<AttendanceLesson> updatedLessons, long dayId) {
List<AttendanceLesson> lessonsFromApiEntities = DataObjectConverter
.lessonsToAttendanceLessonsEntities(lessons);
for (AttendanceLesson apiLessonEntity : lessonsFromApiEntities) {
AttendanceLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId);
apiLessonEntity.setDayId(dayId);
if (lessonFromDb != null) {
apiLessonEntity.setId(lessonFromDb.getId());
}
if (!"".equals(apiLessonEntity.getSubject())) {
updatedLessons.add(apiLessonEntity);
}
}
}
private AttendanceLesson getLessonFromDb(AttendanceLesson apiEntity, long dayId) {
return daoSession.getAttendanceLessonDao().queryBuilder()
.where(AttendanceLessonDao.Properties.DayId.eq(dayId),
AttendanceLessonDao.Properties.Date.eq(apiEntity.getDate()),
AttendanceLessonDao.Properties.Number.eq(apiEntity.getNumber()))
.unique();
}
}

View File

@ -0,0 +1,13 @@
package io.github.wulkanowy.data.sync.attendance;
import java.io.IOException;
import java.text.ParseException;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
public interface AttendanceSyncContract {
void syncAttendance(String date) throws NotLoggedInErrorException, IOException, ParseException;
void syncAttendance() throws NotLoggedInErrorException, IOException, ParseException;
}

View File

@ -9,12 +9,13 @@ import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan; import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.generic.Lesson;
import io.github.wulkanowy.api.login.NotLoggedInErrorException; import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.data.db.dao.entities.DaoSession; import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Day; import io.github.wulkanowy.data.db.dao.entities.Day;
import io.github.wulkanowy.data.db.dao.entities.DayDao; import io.github.wulkanowy.data.db.dao.entities.DayDao;
import io.github.wulkanowy.data.db.dao.entities.Lesson; import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
import io.github.wulkanowy.data.db.dao.entities.LessonDao; import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao;
import io.github.wulkanowy.data.db.dao.entities.Week; import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao; import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract; import io.github.wulkanowy.data.db.shared.SharedPrefContract;
@ -27,9 +28,11 @@ public class TimetableSync implements TimetableSyncContract {
private final DaoSession daoSession; private final DaoSession daoSession;
private final SharedPrefContract sharedPref;
private final Vulcan vulcan; private final Vulcan vulcan;
private final SharedPrefContract sharedPref; private long userId;
@Inject @Inject
TimetableSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) { TimetableSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) {
@ -38,83 +41,122 @@ public class TimetableSync implements TimetableSyncContract {
this.vulcan = vulcan; this.vulcan = vulcan;
} }
@Override
public void syncTimetable(String date) throws NotLoggedInErrorException, IOException, ParseException {
long userId = sharedPref.getCurrentUserId();
io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> weekFromNet =
date == null ? vulcan.getTimetable().getWeekTable()
: vulcan.getTimetable().getWeekTable(String.valueOf(TimeUtils.getNetTicks(date)));
Week weekFromDb = daoSession.getWeekDao().queryBuilder()
.where(WeekDao.Properties.UserId.eq(userId),
WeekDao.Properties.StartDayDate.eq(weekFromNet.getStartDayDate()))
.unique();
Long weekId;
if (weekFromDb == null) {
Week weekFromNetEntity = DataObjectConverter.weekToWeekEntity(weekFromNet).setUserId(userId);
weekId = daoSession.getWeekDao().insert(weekFromNetEntity);
} else {
weekId = weekFromDb.getId();
}
List<io.github.wulkanowy.api.generic.Day> dayListFromNet = weekFromNet.getDays();
List<Lesson> updatedLessonList = new ArrayList<>();
for (io.github.wulkanowy.api.generic.Day dayFromNet : dayListFromNet) {
Day dayFromNetEntity = DataObjectConverter.dayToDayEntity(dayFromNet);
Day dayFromDb = daoSession.getDayDao().queryBuilder()
.where(DayDao.Properties.UserId.eq(userId),
DayDao.Properties.WeekId.eq(weekId),
DayDao.Properties.Date.eq(dayFromNetEntity.getDate()))
.unique();
dayFromNetEntity.setUserId(userId);
dayFromNetEntity.setWeekId(weekId);
Long dayId;
if (dayFromDb != null) {
dayFromNetEntity.setId(dayFromDb.getId());
daoSession.getDayDao().save(dayFromNetEntity);
dayId = dayFromNetEntity.getId();
} else {
dayId = daoSession.getDayDao().insert(dayFromNetEntity);
}
List<Lesson> lessonListFromNetEntities = DataObjectConverter
.lessonsToLessonsEntities(dayFromNet.getLessons());
for (Lesson lessonFromNetEntity : lessonListFromNetEntities) {
Lesson lessonFromDb = daoSession.getLessonDao().queryBuilder()
.where(LessonDao.Properties.DayId.eq(dayId),
LessonDao.Properties.Date.eq(lessonFromNetEntity.getDate()),
LessonDao.Properties.StartTime.eq(lessonFromNetEntity.getStartTime()),
LessonDao.Properties.EndTime.eq(lessonFromNetEntity.getEndTime()))
.unique();
if (lessonFromDb != null) {
lessonFromNetEntity.setId(lessonFromDb.getId());
}
lessonFromNetEntity.setDayId(dayFromNetEntity.getId());
if (!"".equals(lessonFromNetEntity.getSubject())) {
updatedLessonList.add(lessonFromNetEntity);
}
}
}
daoSession.getLessonDao().saveInTx(updatedLessonList);
LogUtils.debug("Synchronization lessons (amount = " + updatedLessonList.size() + ")");
}
@Override @Override
public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException { public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException {
syncTimetable(null); syncTimetable(null);
} }
@Override
public void syncTimetable(String date) throws NotLoggedInErrorException, IOException, ParseException {
this.userId = sharedPref.getCurrentUserId();
io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> weekApi = getWeekFromApi(getNormalizedDate(date));
Week weekDb = getWeekFromDb(weekApi.getStartDayDate());
long weekId = updateWeekInDb(weekDb, weekApi);
List<TimetableLesson> lessonList = updateDays(weekApi.getDays(), weekId);
daoSession.getTimetableLessonDao().saveInTx(lessonList);
LogUtils.debug("Synchronization lessons (amount = " + lessonList.size() + ")");
}
private String getNormalizedDate(String date) throws ParseException {
return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : "";
}
private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> getWeekFromApi(String date)
throws IOException, NotLoggedInErrorException, ParseException {
return vulcan.getTimetable().getWeekTable(date);
}
private Week getWeekFromDb(String date) {
return daoSession.getWeekDao()
.queryBuilder()
.where(WeekDao.Properties.UserId.eq(userId), WeekDao.Properties.StartDayDate.eq(date))
.unique();
}
private Long updateWeekInDb(Week dbEntity, io.github.wulkanowy.api.generic.Week fromApi) {
if (dbEntity != null) {
dbEntity.setIsTimetableSynced(true);
dbEntity.update();
return dbEntity.getId();
}
Week apiEntity = DataObjectConverter.weekToWeekEntity(fromApi).setUserId(userId);
apiEntity.setIsTimetableSynced(true);
return daoSession.getWeekDao().insert(apiEntity);
}
private List<TimetableLesson> updateDays(List<io.github.wulkanowy.api.generic.Day> dayListFromApi, long weekId) {
List<TimetableLesson> updatedLessonList = new ArrayList<>();
for (io.github.wulkanowy.api.generic.Day dayFromApi : dayListFromApi) {
Day dbDayEntity = getDayFromDb(dayFromApi.getDate());
Day apiDayEntity = DataObjectConverter.dayToDayEntity(dayFromApi);
long dayId = updateDay(dbDayEntity, apiDayEntity, weekId);
updateLessons(dayFromApi.getLessons(), updatedLessonList, dayId);
}
return updatedLessonList;
}
private Day getDayFromDb(String date) {
return daoSession.getDayDao()
.queryBuilder()
.where(DayDao.Properties.UserId.eq(userId), DayDao.Properties.Date.eq(date))
.unique();
}
private long updateDay(Day dayFromDb, Day apiDayEntity, long weekId) {
apiDayEntity.setUserId(userId);
apiDayEntity.setWeekId(weekId);
if (null != dayFromDb) {
apiDayEntity.setId(dayFromDb.getId());
daoSession.getDayDao().save(apiDayEntity);
dayFromDb.refresh();
return dayFromDb.getId();
}
return daoSession.getDayDao().insert(apiDayEntity);
}
private void updateLessons(List<Lesson> lessons, List<TimetableLesson> updatedLessons, long dayId) {
List<TimetableLesson> lessonsFromApiEntities = DataObjectConverter
.lessonsToTimetableLessonsEntities(lessons);
for (TimetableLesson apiLessonEntity : lessonsFromApiEntities) {
TimetableLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId);
apiLessonEntity.setDayId(dayId);
if (lessonFromDb != null) {
apiLessonEntity.setId(lessonFromDb.getId());
}
if (!"".equals(apiLessonEntity.getSubject())) {
updatedLessons.add(apiLessonEntity);
}
}
}
private TimetableLesson getLessonFromDb(TimetableLesson apiEntity, long dayId) {
return daoSession.getTimetableLessonDao().queryBuilder()
.where(TimetableLessonDao.Properties.DayId.eq(dayId),
TimetableLessonDao.Properties.Date.eq(apiEntity.getDate()),
TimetableLessonDao.Properties.StartTime.eq(apiEntity.getStartTime()),
TimetableLessonDao.Properties.EndTime.eq(apiEntity.getEndTime()))
.unique();
}
} }

View File

@ -4,6 +4,7 @@ import dagger.Component;
import io.github.wulkanowy.di.annotations.PerFragment; import io.github.wulkanowy.di.annotations.PerFragment;
import io.github.wulkanowy.di.modules.FragmentModule; import io.github.wulkanowy.di.modules.FragmentModule;
import io.github.wulkanowy.ui.main.attendance.AttendanceFragment; import io.github.wulkanowy.ui.main.attendance.AttendanceFragment;
import io.github.wulkanowy.ui.main.attendance.AttendanceTabFragment;
import io.github.wulkanowy.ui.main.dashboard.DashboardFragment; import io.github.wulkanowy.ui.main.dashboard.DashboardFragment;
import io.github.wulkanowy.ui.main.grades.GradesFragment; import io.github.wulkanowy.ui.main.grades.GradesFragment;
import io.github.wulkanowy.ui.main.timetable.TimetableFragment; import io.github.wulkanowy.ui.main.timetable.TimetableFragment;
@ -17,6 +18,8 @@ public interface FragmentComponent {
void inject(AttendanceFragment attendanceFragment); void inject(AttendanceFragment attendanceFragment);
void inject(AttendanceTabFragment attendanceTabFragment);
void inject(DashboardFragment dashboardFragment); void inject(DashboardFragment dashboardFragment);
void inject(TimetableFragment timetableFragment); void inject(TimetableFragment timetableFragment);

View File

@ -21,6 +21,8 @@ import io.github.wulkanowy.data.db.resources.ResourcesContract;
import io.github.wulkanowy.data.db.shared.SharedPref; import io.github.wulkanowy.data.db.shared.SharedPref;
import io.github.wulkanowy.data.db.shared.SharedPrefContract; import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.data.sync.SyncContract; import io.github.wulkanowy.data.sync.SyncContract;
import io.github.wulkanowy.data.sync.attendance.AttendanceSync;
import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract;
import io.github.wulkanowy.data.sync.grades.GradeSync; import io.github.wulkanowy.data.sync.grades.GradeSync;
import io.github.wulkanowy.data.sync.login.LoginSync; import io.github.wulkanowy.data.sync.login.LoginSync;
import io.github.wulkanowy.data.sync.login.LoginSyncContract; import io.github.wulkanowy.data.sync.login.LoginSyncContract;
@ -122,6 +124,12 @@ public class ApplicationModule {
return timetableSync; return timetableSync;
} }
@Singleton
@Provides
AttendanceSyncContract provideAttendanceSync(AttendanceSync attendanceSync) {
return attendanceSync;
}
@Provides @Provides
FirebaseJobDispatcher provideDispatcher() { FirebaseJobDispatcher provideDispatcher() {
return new FirebaseJobDispatcher(new GooglePlayDriver(application)); return new FirebaseJobDispatcher(new GooglePlayDriver(application));

View File

@ -7,7 +7,11 @@ import dagger.Provides;
import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.FlexibleAdapter;
import io.github.wulkanowy.di.annotations.PerFragment; import io.github.wulkanowy.di.annotations.PerFragment;
import io.github.wulkanowy.ui.main.attendance.AttendanceContract; import io.github.wulkanowy.ui.main.attendance.AttendanceContract;
import io.github.wulkanowy.ui.main.attendance.AttendanceHeaderItem;
import io.github.wulkanowy.ui.main.attendance.AttendancePagerAdapter;
import io.github.wulkanowy.ui.main.attendance.AttendancePresenter; import io.github.wulkanowy.ui.main.attendance.AttendancePresenter;
import io.github.wulkanowy.ui.main.attendance.AttendanceTabContract;
import io.github.wulkanowy.ui.main.attendance.AttendanceTabPresenter;
import io.github.wulkanowy.ui.main.dashboard.DashboardContract; import io.github.wulkanowy.ui.main.dashboard.DashboardContract;
import io.github.wulkanowy.ui.main.dashboard.DashboardPresenter; import io.github.wulkanowy.ui.main.dashboard.DashboardPresenter;
import io.github.wulkanowy.ui.main.grades.GradeHeaderItem; import io.github.wulkanowy.ui.main.grades.GradeHeaderItem;
@ -47,6 +51,22 @@ public class FragmentModule {
return dashboardPresenter; return dashboardPresenter;
} }
@PerFragment
@Provides
AttendanceTabContract.Presenter provideAttendanceTabPresenter(AttendanceTabPresenter timetableTabPresenter) {
return timetableTabPresenter;
}
@Provides
AttendancePagerAdapter provideAttendancePagerAdapter() {
return new AttendancePagerAdapter(fragment.getChildFragmentManager());
}
@Provides
FlexibleAdapter<AttendanceHeaderItem> provideAttendanceTabAdapter() {
return new FlexibleAdapter<>(null);
}
@PerFragment @PerFragment
@Provides @Provides
TimetableContract.Presenter provideTimetablePresenter(TimetablePresenter timetablePresenter) { TimetableContract.Presenter provideTimetablePresenter(TimetablePresenter timetablePresenter) {
@ -65,12 +85,12 @@ public class FragmentModule {
} }
@Provides @Provides
FlexibleAdapter<GradeHeaderItem> provideGradesAdapter() { FlexibleAdapter<TimetableHeaderItem> provideTimetableTabAdapter() {
return new FlexibleAdapter<>(null); return new FlexibleAdapter<>(null);
} }
@Provides @Provides
FlexibleAdapter<TimetableHeaderItem> provideTimetableTabAdapter() { FlexibleAdapter<GradeHeaderItem> provideGradesAdapter() {
return new FlexibleAdapter<>(null); return new FlexibleAdapter<>(null);
} }
} }

View File

@ -18,6 +18,7 @@ import butterknife.ButterKnife;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.services.SyncJob; import io.github.wulkanowy.services.SyncJob;
import io.github.wulkanowy.ui.base.BaseActivity; import io.github.wulkanowy.ui.base.BaseActivity;
import io.github.wulkanowy.ui.main.attendance.AttendanceFragment;
import io.github.wulkanowy.ui.main.dashboard.DashboardFragment; import io.github.wulkanowy.ui.main.dashboard.DashboardFragment;
import io.github.wulkanowy.ui.main.grades.GradesFragment; import io.github.wulkanowy.ui.main.grades.GradesFragment;
import io.github.wulkanowy.ui.main.timetable.TimetableFragment; import io.github.wulkanowy.ui.main.timetable.TimetableFragment;
@ -134,7 +135,7 @@ public class MainActivity extends BaseActivity implements MainContract.View,
private void initiationViewPager() { private void initiationViewPager() {
pagerAdapter.addFragment(new GradesFragment()); pagerAdapter.addFragment(new GradesFragment());
pagerAdapter.addFragment(new DashboardFragment()); pagerAdapter.addFragment(new AttendanceFragment());
pagerAdapter.addFragment(new DashboardFragment()); pagerAdapter.addFragment(new DashboardFragment());
pagerAdapter.addFragment(new TimetableFragment()); pagerAdapter.addFragment(new TimetableFragment());
pagerAdapter.addFragment(new DashboardFragment()); pagerAdapter.addFragment(new DashboardFragment());

View File

@ -1,35 +1,35 @@
package io.github.wulkanowy.ui.main.timetable; package io.github.wulkanowy.ui.main;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
class TabsData { public class TabsData {
private List<Fragment> fragments = new ArrayList<>(); private List<Fragment> fragments = new ArrayList<>();
private List<String> titles = new ArrayList<>(); private List<String> titles = new ArrayList<>();
Fragment getFragment(int index) { public Fragment getFragment(int index) {
return fragments.get(index); return fragments.get(index);
} }
void addFragment(Fragment fragment) { public void addFragment(Fragment fragment) {
if (fragment != null) { if (fragment != null) {
fragments.add(fragment); fragments.add(fragment);
} }
} }
int getFragmentsCount() { public int getFragmentsCount() {
return fragments.size(); return fragments.size();
} }
String getTitle(int index) { public String getTitle(int index) {
return titles.get(index); return titles.get(index);
} }
void addTitle(String title) { public void addTitle(String title) {
if (title != null) { if (title != null) {
titles.add(title); titles.add(title);
} }

View File

@ -1,8 +1,9 @@
package io.github.wulkanowy.ui.main.attendance; package io.github.wulkanowy.ui.main.attendance;
import io.github.wulkanowy.di.annotations.PerFragment; import io.github.wulkanowy.di.annotations.PerActivity;
import io.github.wulkanowy.ui.base.BaseContract; import io.github.wulkanowy.ui.base.BaseContract;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.TabsData;
public interface AttendanceContract { public interface AttendanceContract {
@ -10,14 +11,26 @@ public interface AttendanceContract {
void setActivityTitle(); void setActivityTitle();
void scrollViewPagerToPosition(int position);
void setTabDataToAdapter(TabsData tabsData);
void setAdapterWithTabLayout();
void setChildFragmentSelected(int position, boolean selected);
boolean isMenuVisible(); boolean isMenuVisible();
} }
@PerFragment @PerActivity
interface Presenter extends BaseContract.Presenter<View> { interface Presenter extends BaseContract.Presenter<View> {
void onStart(View view, OnFragmentIsReadyListener listener);
void onFragmentVisible(boolean isVisible); void onFragmentVisible(boolean isVisible);
void onTabSelected(int position);
void onTabUnselected(int position);
void onStart(View view, OnFragmentIsReadyListener listener);
} }
} }

View File

@ -0,0 +1,91 @@
package io.github.wulkanowy.ui.main.attendance;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
public class AttendanceDialogFragment extends DialogFragment {
private static final String ARGUMENT_KEY = "Item";
private AttendanceLesson lesson;
@BindView(R.id.attendance_dialog_subject_value)
TextView subject;
@BindView(R.id.attendance_dialog_date_value)
TextView date;
@BindView(R.id.attendance_dialog_number_value)
TextView number;
@BindView(R.id.attendance_dialog_description_value)
TextView description;
public AttendanceDialogFragment() {
//empty constructor for fragment
}
public static AttendanceDialogFragment newInstance(AttendanceLesson lesson) {
AttendanceDialogFragment dialogFragment = new AttendanceDialogFragment();
Bundle bundle = new Bundle();
bundle.putSerializable(ARGUMENT_KEY, lesson);
dialogFragment.setArguments(bundle);
return dialogFragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
lesson = (AttendanceLesson) getArguments().getSerializable(ARGUMENT_KEY);
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.attendance_dialog, container, false);
ButterKnife.bind(this, view);
if (!lesson.getSubject().isEmpty()) {
subject.setText(lesson.getSubject());
}
if (!lesson.getDate().isEmpty()) {
date.setText(lesson.getDate());
}
if (0 != lesson.getNumber()) {
number.setText(String.valueOf(lesson.getNumber()));
}
description.setText(lesson.getDescription());
if (lesson.getIsAbsenceUnexcused()) {
description.setTextColor(getResources().getColor(R.color.colorPrimaryDark));
}
return view;
}
@OnClick(R.id.attendance_dialog_close)
void onClickCloseButton() {
dismiss();
}
}

View File

@ -2,30 +2,42 @@ package io.github.wulkanowy.ui.main.attendance;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import javax.inject.Inject; import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.di.component.FragmentComponent; import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.ui.base.BaseFragment; import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.TabsData;
public class AttendanceFragment extends BaseFragment implements AttendanceContract.View { public class AttendanceFragment extends BaseFragment implements AttendanceContract.View, TabLayout.OnTabSelectedListener {
@BindView(R.id.attendance_fragment_viewpager)
ViewPager viewPager;
@BindView(R.id.attendance_fragment_tab_layout)
TabLayout tabLayout;
@Inject
AttendancePagerAdapter pagerAdapter;
@Inject @Inject
AttendanceContract.Presenter presenter; AttendanceContract.Presenter presenter;
public AttendanceFragment() { @Nullable
// empty constructor for fragment
}
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_attendance, container, false); View view = inflater.inflate(R.layout.fragment_attendance, container, false);
FragmentComponent component = getFragmentComponent(); FragmentComponent component = getFragmentComponent();
@ -46,14 +58,60 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
} }
} }
@Override
public void onTabSelected(TabLayout.Tab tab) {
presenter.onTabSelected(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
presenter.onTabUnselected(tab.getPosition());
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
//do nothing
}
@Override
public void setTabDataToAdapter(TabsData tabsData) {
pagerAdapter.setTabsData(tabsData);
}
@Override
public void setAdapterWithTabLayout() {
viewPager.setAdapter(pagerAdapter);
tabLayout.setupWithViewPager(viewPager);
tabLayout.addOnTabSelectedListener(this);
}
@Override
public void setChildFragmentSelected(int position, boolean selected) {
((AttendanceTabFragment) pagerAdapter.getItem(position)).setSelected(selected);
}
@Override
public void scrollViewPagerToPosition(int position) {
viewPager.setCurrentItem(position, false);
}
@Override @Override
public void setActivityTitle() { public void setActivityTitle() {
setTitle(getString(R.string.dashboard_text)); setTitle(getString(R.string.attendance_text));
}
@Override
public void onError(String message) {
if (getActivity() != null) {
Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager),
message, Snackbar.LENGTH_LONG).show();
}
} }
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView();
presenter.onDestroy(); presenter.onDestroy();
super.onDestroyView();
} }
} }

View File

@ -0,0 +1,136 @@
package io.github.wulkanowy.ui.main.attendance;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.util.List;
import butterknife.BindColor;
import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem;
import eu.davidea.viewholders.ExpandableViewHolder;
import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.data.db.dao.entities.Day;
public class AttendanceHeaderItem
extends AbstractExpandableHeaderItem<AttendanceHeaderItem.HeaderViewHolder, AttendanceSubItem> {
private Day day;
AttendanceHeaderItem(Day day) {
this.day = day;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AttendanceHeaderItem that = (AttendanceHeaderItem) o;
return new EqualsBuilder()
.append(day, that.day)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(day)
.toHashCode();
}
@Override
public int getLayoutRes() {
return R.layout.attendance_header;
}
@Override
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
return new HeaderViewHolder(view, adapter);
}
@Override
public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List payloads) {
holder.onBind(day, getSubItems());
}
static class HeaderViewHolder extends ExpandableViewHolder {
@BindView(R.id.attendance_header_day)
TextView dayName;
@BindView(R.id.attendance_header_date)
TextView date;
@BindView(R.id.attendance_header_description)
TextView description;
@BindView(R.id.attendance_header_alert_image)
ImageView alert;
@BindView(R.id.attendance_header_free_name)
TextView freeName;
@BindColor(R.color.secondary_text)
int secondaryColor;
@BindColor(R.color.free_day)
int backgroundFreeDay;
HeaderViewHolder(View view, FlexibleAdapter adapter) {
super(view, adapter);
view.setOnClickListener(this);
ButterKnife.bind(this, view);
}
void onBind(Day item, List<AttendanceSubItem> subItems) {
dayName.setText(StringUtils.capitalize(item.getDayName()));
date.setText(item.getDate());
int numberOfHours = countNotPresentHours(subItems);
description.setText((getContentView().getResources().getQuantityString(R.plurals.numberOfAbsences,
numberOfHours, numberOfHours)));
description.setVisibility(numberOfHours > 0 ? View.VISIBLE : View.INVISIBLE);
alert.setVisibility(isSubItemsHasChanges(subItems) ? View.VISIBLE : View.INVISIBLE);
freeName.setVisibility(subItems.isEmpty() ? View.VISIBLE : View.INVISIBLE);
if (item.getAttendanceLessons().isEmpty()) {
((FrameLayout) getContentView()).setForeground(null);
getContentView().setBackgroundColor(backgroundFreeDay);
dayName.setTextColor(secondaryColor);
}
}
private int countNotPresentHours(List<AttendanceSubItem> subItems) {
int i = 0;
for (AttendanceSubItem subItem : subItems) {
if (subItem.getLesson().getIsAbsenceUnexcused()) {
i++;
}
}
return i;
}
private boolean isSubItemsHasChanges(List<AttendanceSubItem> subItems) {
for (AttendanceSubItem subItem : subItems) {
if (subItem.getLesson().getIsAbsenceUnexcused() || subItem.getLesson().getIsUnexcusedLateness()) {
return true;
}
}
return false;
}
}
}

View File

@ -0,0 +1,38 @@
package io.github.wulkanowy.ui.main.attendance;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import io.github.wulkanowy.ui.main.TabsData;
public class AttendancePagerAdapter extends FragmentStatePagerAdapter {
private TabsData tabsData;
public AttendancePagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
void setTabsData(TabsData tabsData) {
this.tabsData = tabsData;
}
@Override
public Fragment getItem(int position) {
return tabsData.getFragment(position);
}
@Override
public int getCount() {
return tabsData.getFragmentsCount();
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return tabsData.getTitle(position);
}
}

View File

@ -1,16 +1,33 @@
package io.github.wulkanowy.ui.main.attendance; package io.github.wulkanowy.ui.main.attendance;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.ui.base.BasePresenter; import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.TabsData;
import io.github.wulkanowy.utils.TimeUtils;
import io.github.wulkanowy.utils.async.AbstractTask;
import io.github.wulkanowy.utils.async.AsyncListeners;
public class AttendancePresenter extends BasePresenter<AttendanceContract.View> public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
implements AttendanceContract.Presenter { implements AttendanceContract.Presenter, AsyncListeners.OnFirstLoadingListener {
private AbstractTask loadingTask;
private List<String> dates = new ArrayList<>();
private TabsData tabsData = new TabsData();
private OnFragmentIsReadyListener listener; private OnFragmentIsReadyListener listener;
private int positionToScroll;
private boolean isFirstSight = false;
@Inject @Inject
AttendancePresenter(RepositoryContract repository) { AttendancePresenter(RepositoryContract repository) {
super(repository); super(repository);
@ -25,7 +42,18 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
getView().setActivityTitle(); getView().setActivityTitle();
} }
this.listener.onFragmentIsReady(); if (dates.isEmpty()) {
dates = TimeUtils.getMondaysFromCurrentSchoolYear();
}
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
if (!isFirstSight) {
isFirstSight = true;
loadingTask = new AbstractTask();
loadingTask.setOnFirstLoadingListener(this);
loadingTask.execute();
}
} }
@Override @Override
@ -34,4 +62,50 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
getView().setActivityTitle(); getView().setActivityTitle();
} }
} }
@Override
public void onTabSelected(int position) {
getView().setChildFragmentSelected(position, true);
}
@Override
public void onTabUnselected(int position) {
getView().setChildFragmentSelected(position, false);
}
@Override
public void onDoInBackgroundLoading() throws Exception {
for (String date : dates) {
tabsData.addTitle(date);
tabsData.addFragment(AttendanceTabFragment.newInstance(date));
}
}
@Override
public void onCanceledLoadingAsync() {
//do nothing
}
@Override
public void onEndLoadingAsync(boolean result, Exception exception) {
if (result) {
getView().setTabDataToAdapter(tabsData);
getView().setAdapterWithTabLayout();
getView().scrollViewPagerToPosition(positionToScroll);
listener.onFragmentIsReady();
}
}
@Override
public void onDestroy() {
isFirstSight = false;
if (loadingTask != null) {
loadingTask.cancel(true);
loadingTask = null;
}
super.onDestroy();
}
} }

View File

@ -0,0 +1,119 @@
package io.github.wulkanowy.ui.main.attendance;
import android.content.Context;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractSectionableItem;
import eu.davidea.viewholders.FlexibleViewHolder;
import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
class AttendanceSubItem
extends AbstractSectionableItem<AttendanceSubItem.SubItemViewHolder, AttendanceHeaderItem> {
private AttendanceLesson lesson;
AttendanceSubItem(AttendanceHeaderItem header, AttendanceLesson lesson) {
super(header);
this.lesson = lesson;
}
AttendanceLesson getLesson() {
return lesson;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AttendanceSubItem that = (AttendanceSubItem) o;
return new EqualsBuilder()
.append(lesson, that.lesson)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(lesson)
.toHashCode();
}
@Override
public int getLayoutRes() {
return R.layout.attendance_subitem;
}
@Override
public AttendanceSubItem.SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
return new AttendanceSubItem.SubItemViewHolder(view, adapter);
}
@Override
public void bindViewHolder(FlexibleAdapter adapter, AttendanceSubItem.SubItemViewHolder holder, int position, List payloads) {
holder.onBind(lesson);
}
static class SubItemViewHolder extends FlexibleViewHolder {
@BindView(R.id.attendance_subItem_lesson)
TextView lessonName;
@BindView(R.id.attendance_subItem_number)
TextView lessonNumber;
@BindView(R.id.attendance_subItem_description)
TextView lessonDescription;
@BindView(R.id.attendance_subItem_alert_image)
ImageView alert;
private Context context;
private AttendanceLesson item;
SubItemViewHolder(View view, FlexibleAdapter adapter) {
super(view, adapter);
ButterKnife.bind(this, view);
context = view.getContext();
view.setOnClickListener(this);
}
void onBind(AttendanceLesson lesson) {
item = lesson;
lessonName.setText(lesson.getSubject());
lessonNumber.setText((String.valueOf(lesson.getNumber())));
lessonDescription.setText(lesson.getDescription());
alert.setVisibility(lesson.getIsAbsenceUnexcused() || lesson.getIsUnexcusedLateness()
? View.VISIBLE : View.INVISIBLE);
}
@Override
public void onClick(View view) {
super.onClick(view);
showDialog();
}
private void showDialog() {
AttendanceDialogFragment dialogFragment = AttendanceDialogFragment.newInstance(item);
dialogFragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0);
dialogFragment.show(((FragmentActivity) context).getSupportFragmentManager(), item.toString());
}
}
}

View File

@ -0,0 +1,32 @@
package io.github.wulkanowy.ui.main.attendance;
import java.util.List;
import io.github.wulkanowy.ui.base.BaseContract;
public interface AttendanceTabContract {
interface View extends BaseContract.View {
void updateAdapterList(List<AttendanceHeaderItem> headerItems);
void onRefreshSuccess();
void hideRefreshingBar();
void showNoItem(boolean show);
void showProgressBar(boolean show);
}
interface Presenter extends BaseContract.Presenter<AttendanceTabContract.View> {
void onFragmentSelected(boolean isSelected);
void setArgumentDate(String date);
void onStart(AttendanceTabContract.View view, boolean isPrimary);
void onRefresh();
}
}

View File

@ -0,0 +1,170 @@
package io.github.wulkanowy.ui.main.attendance;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
import io.github.wulkanowy.R;
import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.ui.base.BaseFragment;
public class AttendanceTabFragment extends BaseFragment implements AttendanceTabContract.View,
SwipeRefreshLayout.OnRefreshListener {
private static final String ARGUMENT_KEY = "date";
private static final String SAVED_KEY = "isSelected";
private boolean isPrimary = false;
private boolean isSelected = false;
@BindView(R.id.attendance_tab_fragment_recycler)
RecyclerView recyclerView;
@BindView(R.id.attendance_tab_fragment_swipe_refresh)
SwipeRefreshLayout refreshLayout;
@BindView(R.id.attendance_tab_fragment_progress_bar)
View progressBar;
@BindView(R.id.attendance_tab_fragment_no_item_container)
View noItemView;
@Inject
AttendanceTabContract.Presenter presenter;
@Inject
FlexibleAdapter<AttendanceHeaderItem> adapter;
public static AttendanceTabFragment newInstance(String date) {
AttendanceTabFragment fragmentTab = new AttendanceTabFragment();
Bundle bundle = new Bundle();
bundle.putString(ARGUMENT_KEY, date);
fragmentTab.setArguments(bundle);
return fragmentTab;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
isSelected = savedInstanceState.getBoolean(SAVED_KEY, isSelected);
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_attendance_tab, container, false);
FragmentComponent component = getFragmentComponent();
if (component != null) {
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
if (getArguments() != null) {
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
}
presenter.onStart(this, isPrimary);
}
return view;
}
@Override
protected void setUpOnViewCreated(View fragmentView) {
adapter.setAutoCollapseOnExpand(true);
adapter.setAutoScrollOnExpand(true);
adapter.expandItemsAtStartUp();
recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext()));
recyclerView.setAdapter(adapter);
refreshLayout.setColorSchemeResources(android.R.color.black);
refreshLayout.setOnRefreshListener(this);
}
@Override
public void updateAdapterList(List<AttendanceHeaderItem> headerItems) {
adapter.updateDataSet(headerItems);
}
@Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
if (presenter != null && getView() != null) {
presenter.onFragmentSelected(isSelected);
} else if (isSelected) {
isPrimary = true;
}
}
@Override
public void onRefresh() {
presenter.onRefresh();
}
@Override
public void onRefreshSuccess() {
onError(R.string.timetable_refresh_success);
}
@Override
public void hideRefreshingBar() {
refreshLayout.setRefreshing(false);
}
@Override
public void showProgressBar(boolean show) {
progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}
@Override
public void showNoItem(boolean show) {
noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}
public void setSelected(boolean selected) {
isSelected = selected;
}
@Override
public void onError(String message) {
if (getActivity() != null) {
Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager),
message, Snackbar.LENGTH_LONG).show();
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(SAVED_KEY, isSelected);
super.onSaveInstanceState(outState);
}
@Override
public void onDestroyView() {
isPrimary = false;
presenter.onDestroy();
super.onDestroyView();
}
}

View File

@ -0,0 +1,175 @@
package io.github.wulkanowy.ui.main.attendance;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.data.db.dao.entities.Day;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.utils.async.AbstractTask;
import io.github.wulkanowy.utils.async.AsyncListeners;
public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.View>
implements AttendanceTabContract.Presenter, AsyncListeners.OnRefreshListener,
AsyncListeners.OnFirstLoadingListener {
private AbstractTask refreshTask;
private AbstractTask loadingTask;
private List<AttendanceHeaderItem> headerItems = new ArrayList<>();
private String date;
private boolean isFirstSight = false;
@Inject
AttendanceTabPresenter(RepositoryContract repository) {
super(repository);
}
@Override
public void onStart(AttendanceTabContract.View view, boolean isPrimary) {
super.onStart(view);
getView().showProgressBar(true);
getView().showNoItem(false);
onFragmentSelected(isPrimary);
}
@Override
public void onFragmentSelected(boolean isSelected) {
if (!isFirstSight && isSelected) {
isFirstSight = true;
loadingTask = new AbstractTask();
loadingTask.setOnFirstLoadingListener(this);
loadingTask.execute();
}
}
@Override
public void onRefresh() {
if (getView().isNetworkConnected()) {
refreshTask = new AbstractTask();
refreshTask.setOnRefreshListener(this);
refreshTask.execute();
} else {
getView().onNoNetworkError();
getView().hideRefreshingBar();
}
}
@Override
public void onDoInBackgroundRefresh() throws Exception {
syncData();
}
@Override
public void onCanceledRefreshAsync() {
if (isViewAttached()) {
getView().hideRefreshingBar();
}
}
@Override
public void onEndRefreshAsync(boolean result, Exception exception) {
if (result) {
loadingTask = new AbstractTask();
loadingTask.setOnFirstLoadingListener(this);
loadingTask.execute();
getView().onRefreshSuccess();
} else {
getView().onError(getRepository().getErrorLoginMessage(exception));
}
getView().hideRefreshingBar();
}
@Override
public void onDoInBackgroundLoading() throws Exception {
Week week = getRepository().getWeek(date);
if (week == null || !week.getIsAttendanceSynced()) {
syncData();
week = getRepository().getWeek(date);
}
List<Day> dayList = week.getDayList();
headerItems = new ArrayList<>();
boolean isEmptyWeek = true;
for (Day day : dayList) {
AttendanceHeaderItem headerItem = new AttendanceHeaderItem(day);
if (isEmptyWeek) {
isEmptyWeek = day.getAttendanceLessons().isEmpty();
}
List<AttendanceLesson> lessonList = day.getAttendanceLessons();
List<AttendanceSubItem> subItems = new ArrayList<>();
for (AttendanceLesson lesson : lessonList) {
lesson.setDescription(getRepository().getAttendanceLessonDescription(lesson));
subItems.add(new AttendanceSubItem(headerItem, lesson));
}
headerItem.setSubItems(subItems);
headerItem.setExpanded(false);
headerItems.add(headerItem);
}
if (isEmptyWeek) {
headerItems = new ArrayList<>();
}
}
@Override
public void onCanceledLoadingAsync() {
// do nothing
}
@Override
public void onEndLoadingAsync(boolean result, Exception exception) {
if (headerItems.isEmpty()) {
getView().showNoItem(true);
getView().updateAdapterList(null);
} else {
getView().updateAdapterList(headerItems);
getView().showNoItem(false);
}
getView().showProgressBar(false);
}
@Override
public void setArgumentDate(String date) {
this.date = date;
}
private void syncData() throws Exception {
getRepository().loginCurrentUser();
getRepository().syncAttendance(date);
}
@Override
public void onDestroy() {
isFirstSight = false;
if (refreshTask != null) {
refreshTask.cancel(true);
refreshTask = null;
}
if (loadingTask != null) {
loadingTask.cancel(true);
loadingTask = null;
}
super.onDestroy();
}
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.main.timetable;
import io.github.wulkanowy.di.annotations.PerActivity; import io.github.wulkanowy.di.annotations.PerActivity;
import io.github.wulkanowy.ui.base.BaseContract; import io.github.wulkanowy.ui.base.BaseContract;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.TabsData;
public interface TimetableContract { public interface TimetableContract {

View File

@ -15,13 +15,13 @@ import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.OnClick; import butterknife.OnClick;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.Lesson; import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
public class TimetableDialogFragment extends DialogFragment { public class TimetableDialogFragment extends DialogFragment {
private static final String ARGUMENT_KEY = "Item"; private static final String ARGUMENT_KEY = "Item";
private Lesson lesson; private TimetableLesson lesson;
@BindView(R.id.timetable_dialog_lesson_value) @BindView(R.id.timetable_dialog_lesson_value)
TextView lessonName; TextView lessonName;
@ -54,7 +54,7 @@ public class TimetableDialogFragment extends DialogFragment {
//empty constructor for fragment //empty constructor for fragment
} }
public static TimetableDialogFragment newInstance(Lesson lesson) { public static TimetableDialogFragment newInstance(TimetableLesson lesson) {
TimetableDialogFragment dialogFragment = new TimetableDialogFragment(); TimetableDialogFragment dialogFragment = new TimetableDialogFragment();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
@ -69,7 +69,7 @@ public class TimetableDialogFragment extends DialogFragment {
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (getArguments() != null) { if (getArguments() != null) {
lesson = (Lesson) getArguments().getSerializable(ARGUMENT_KEY); lesson = (TimetableLesson) getArguments().getSerializable(ARGUMENT_KEY);
} }
} }

View File

@ -18,6 +18,7 @@ import io.github.wulkanowy.R;
import io.github.wulkanowy.di.component.FragmentComponent; import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.ui.base.BaseFragment; import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.TabsData;
public class TimetableFragment extends BaseFragment implements TimetableContract.View, TabLayout.OnTabSelectedListener { public class TimetableFragment extends BaseFragment implements TimetableContract.View, TabLayout.OnTabSelectedListener {

View File

@ -94,10 +94,10 @@ public class TimetableHeaderItem
dayName.setText(StringUtils.capitalize(item.getDayName())); dayName.setText(StringUtils.capitalize(item.getDayName()));
date.setText(item.getDate()); date.setText(item.getDate());
alert.setVisibility(isSubItemNewMovedInOrChanged(subItems) ? View.VISIBLE : View.INVISIBLE); alert.setVisibility(isSubItemNewMovedInOrChanged(subItems) ? View.VISIBLE : View.INVISIBLE);
freeName.setVisibility(item.isFreeDay() ? View.VISIBLE : View.INVISIBLE); freeName.setVisibility(item.getIsFreeDay() ? View.VISIBLE : View.INVISIBLE);
freeName.setText(item.getFreeDayName()); freeName.setText(item.getFreeDayName());
if (item.isFreeDay()) { if (item.getIsFreeDay()) {
((FrameLayout) getContentView()).setForeground(null); ((FrameLayout) getContentView()).setForeground(null);
getContentView().setBackgroundColor(backgroundFreeDay); getContentView().setBackgroundColor(backgroundFreeDay);
dayName.setTextColor(secondaryColor); dayName.setTextColor(secondaryColor);

View File

@ -5,6 +5,8 @@ import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.app.FragmentStatePagerAdapter;
import io.github.wulkanowy.ui.main.TabsData;
public class TimetablePagerAdapter extends FragmentStatePagerAdapter { public class TimetablePagerAdapter extends FragmentStatePagerAdapter {
private TabsData tabsData; private TabsData tabsData;

View File

@ -8,6 +8,7 @@ import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.ui.base.BasePresenter; import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.TabsData;
import io.github.wulkanowy.utils.TimeUtils; import io.github.wulkanowy.utils.TimeUtils;
import io.github.wulkanowy.utils.async.AbstractTask; import io.github.wulkanowy.utils.async.AbstractTask;
import io.github.wulkanowy.utils.async.AsyncListeners; import io.github.wulkanowy.utils.async.AsyncListeners;

View File

@ -8,6 +8,9 @@ import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.util.List; import java.util.List;
import butterknife.BindView; import butterknife.BindView;
@ -16,26 +19,41 @@ import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractSectionableItem; import eu.davidea.flexibleadapter.items.AbstractSectionableItem;
import eu.davidea.viewholders.FlexibleViewHolder; import eu.davidea.viewholders.FlexibleViewHolder;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.Lesson; import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
public class TimetableSubItem public class TimetableSubItem
extends AbstractSectionableItem<TimetableSubItem.SubItemViewHolder, TimetableHeaderItem> { extends AbstractSectionableItem<TimetableSubItem.SubItemViewHolder, TimetableHeaderItem> {
private Lesson lesson; private TimetableLesson lesson;
public TimetableSubItem(TimetableHeaderItem header, Lesson lesson) { public TimetableSubItem(TimetableHeaderItem header, TimetableLesson lesson) {
super(header); super(header);
this.lesson = lesson; this.lesson = lesson;
} }
public Lesson getLesson() { public TimetableLesson getLesson() {
return lesson; return lesson;
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
return this == o; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TimetableSubItem that = (TimetableSubItem) o;
return new EqualsBuilder()
.append(lesson, that.lesson)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(lesson)
.toHashCode();
} }
@Override @Override
@ -72,7 +90,7 @@ public class TimetableSubItem
private Context context; private Context context;
private Lesson item; private TimetableLesson item;
SubItemViewHolder(View view, FlexibleAdapter adapter) { SubItemViewHolder(View view, FlexibleAdapter adapter) {
super(view, adapter); super(view, adapter);
@ -81,7 +99,7 @@ public class TimetableSubItem
view.setOnClickListener(this); view.setOnClickListener(this);
} }
void onBind(Lesson lesson) { void onBind(TimetableLesson lesson) {
item = lesson; item = lesson;
lessonName.setText(lesson.getSubject()); lessonName.setText(lesson.getSubject());

View File

@ -8,7 +8,7 @@ import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.data.db.dao.entities.Day; import io.github.wulkanowy.data.db.dao.entities.Day;
import io.github.wulkanowy.data.db.dao.entities.Lesson; import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
import io.github.wulkanowy.data.db.dao.entities.Week; import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.ui.base.BasePresenter; import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.utils.async.AbstractTask; import io.github.wulkanowy.utils.async.AbstractTask;
@ -96,7 +96,7 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
public void onDoInBackgroundLoading() throws Exception { public void onDoInBackgroundLoading() throws Exception {
Week week = getRepository().getWeek(date); Week week = getRepository().getWeek(date);
if (week == null) { if (week == null || !week.getIsTimetableSynced()) {
syncData(); syncData();
week = getRepository().getWeek(date); week = getRepository().getWeek(date);
} }
@ -111,14 +111,14 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
TimetableHeaderItem headerItem = new TimetableHeaderItem(day); TimetableHeaderItem headerItem = new TimetableHeaderItem(day);
if (isFreeWeek) { if (isFreeWeek) {
isFreeWeek = day.isFreeDay(); isFreeWeek = day.getIsFreeDay();
} }
List<Lesson> lessonList = day.getLessons(); List<TimetableLesson> lessonList = day.getTimetableLessons();
List<TimetableSubItem> subItems = new ArrayList<>(); List<TimetableSubItem> subItems = new ArrayList<>();
for (Lesson lesson : lessonList) { for (TimetableLesson lesson : lessonList) {
subItems.add(new TimetableSubItem(headerItem, lesson)); subItems.add(new TimetableSubItem(headerItem, lesson));
} }
@ -143,8 +143,10 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
if (headerItems.isEmpty()) { if (headerItems.isEmpty()) {
getView().showNoItem(true); getView().showNoItem(true);
getView().setFreeWeekName(freeWeekName); getView().setFreeWeekName(freeWeekName);
getView().updateAdapterList(null);
} else { } else {
getView().updateAdapterList(headerItems); getView().updateAdapterList(headerItems);
getView().showNoItem(false);
} }
getView().showProgressBar(false); getView().showProgressBar(false);
} }

View File

@ -4,10 +4,11 @@ package io.github.wulkanowy.utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.data.db.dao.entities.Day; import io.github.wulkanowy.data.db.dao.entities.Day;
import io.github.wulkanowy.data.db.dao.entities.Grade; import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.Lesson;
import io.github.wulkanowy.data.db.dao.entities.Subject; import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
import io.github.wulkanowy.data.db.dao.entities.Week; import io.github.wulkanowy.data.db.dao.entities.Week;
public final class DataObjectConverter { public final class DataObjectConverter {
@ -60,7 +61,7 @@ public final class DataObjectConverter {
return new Day() return new Day()
.setDate(day.getDate()) .setDate(day.getDate())
.setDayName(day.getDayName()) .setDayName(day.getDayName())
.setFreeDay(day.isFreeDay()) .setIsFreeDay(day.isFreeDay())
.setFreeDayName(day.getFreeDayName()); .setFreeDayName(day.getFreeDayName());
} }
@ -75,8 +76,8 @@ public final class DataObjectConverter {
return dayEntityList; return dayEntityList;
} }
public static Lesson lessonToLessonEntity(io.github.wulkanowy.api.generic.Lesson lesson) { public static TimetableLesson lessonToTimetableLessonEntity(io.github.wulkanowy.api.generic.Lesson lesson) {
return new Lesson() return new TimetableLesson()
.setNumber(lesson.getNumber()) .setNumber(lesson.getNumber())
.setSubject(lesson.getSubject()) .setSubject(lesson.getSubject())
.setTeacher(lesson.getTeacher()) .setTeacher(lesson.getTeacher())
@ -94,12 +95,36 @@ public final class DataObjectConverter {
.setNewMovedInOrChanged(lesson.isNewMovedInOrChanged()); .setNewMovedInOrChanged(lesson.isNewMovedInOrChanged());
} }
public static List<Lesson> lessonsToLessonsEntities(List<io.github.wulkanowy.api.generic.Lesson> lessonList) { public static AttendanceLesson lessonToAttendanceLessonEntity(io.github.wulkanowy.api.generic.Lesson lesson) {
return new AttendanceLesson()
.setNumber(Integer.valueOf(lesson.getNumber()))
.setSubject(lesson.getSubject())
.setDate(lesson.getDate())
.setIsPresence(lesson.isPresence())
.setIsAbsenceUnexcused(lesson.isAbsenceUnexcused())
.setIsAbsenceExcused(lesson.isAbsenceExcused())
.setIsUnexcusedLateness(lesson.isUnexcusedLateness())
.setIsAbsenceForSchoolReasons(lesson.isAbsenceForSchoolReasons())
.setIsExcusedLateness(lesson.isExcusedLateness())
.setIsExemption(lesson.isExemption());
}
List<Lesson> lessonEntityList = new ArrayList<>(); public static List<TimetableLesson> lessonsToTimetableLessonsEntities(List<io.github.wulkanowy.api.generic.Lesson> lessonList) {
List<TimetableLesson> lessonEntityList = new ArrayList<>();
for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) { for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) {
lessonEntityList.add(lessonToLessonEntity(lesson)); lessonEntityList.add(lessonToTimetableLessonEntity(lesson));
}
return lessonEntityList;
}
public static List<AttendanceLesson> lessonsToAttendanceLessonsEntities(List<io.github.wulkanowy.api.generic.Lesson> lessonList) {
List<AttendanceLesson> lessonEntityList = new ArrayList<>();
for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) {
lessonEntityList.add(lessonToAttendanceLessonEntity(lesson));
} }
return lessonEntityList; return lessonEntityList;
} }

View File

@ -0,0 +1,152 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="300dp"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/attendance_dialog_relative_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:paddingEnd="20dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingStart="20dp"
android:paddingTop="10dp"
tools:ignore="UselessParent">
<TextView
android:id="@+id/attendance_dialog_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_gravity="start"
android:gravity="center_vertical"
android:maxLines="5"
android:minHeight="60dp"
android:minLines="2"
android:paddingTop="10dp"
android:text="@string/generic_dialog_details"
android:textIsSelectable="true"
android:textSize="20sp" />
<TextView
android:id="@+id/attendance_dialog_subject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_details"
android:layout_marginTop="10dp"
android:text="@string/attendance_dialog_subject"
android:textIsSelectable="true"
android:textSize="17sp" />
<TextView
android:id="@+id/attendance_dialog_subject_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_subject"
android:layout_marginTop="3dp"
android:text="@string/generic_app_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/attendance_dialog_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_subject_value"
android:layout_marginTop="10dp"
android:text="@string/attendance_dialog_description"
android:textSize="17sp" />
<TextView
android:id="@+id/attendance_dialog_description_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_description"
android:layout_marginTop="3dp"
android:text="@string/generic_app_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/attendance_dialog_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_description_value"
android:layout_marginTop="10dp"
android:text="@string/attendance_dialog_date"
android:textSize="17sp" />
<TextView
android:id="@+id/attendance_dialog_date_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_date"
android:layout_marginTop="3dp"
android:text="@string/generic_app_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/attendance_dialog_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_date_value"
android:layout_marginTop="10dp"
android:text="@string/attendance_dialog_number"
android:textSize="17sp" />
<TextView
android:id="@+id/attendance_dialog_number_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_number"
android:layout_marginTop="3dp"
android:text="@string/generic_app_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<Button
android:id="@+id/attendance_dialog_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/attendance_dialog_number_value"
android:layout_marginTop="25dp"
android:background="?attr/selectableItemBackground"
android:focusable="true"
android:text="@string/generic_dialog_close"
android:textAllCaps="true"
android:textSize="15sp" />
</RelativeLayout>
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,86 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ic_border"
android:foreground="?attr/selectableItemBackgroundBorderless">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<TextView
android:id="@+id/attendance_header_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginEnd="15dp"
android:layout_marginRight="15dp"
android:layout_toLeftOf="@id/attendance_header_alert_image"
android:layout_toStartOf="@+id/attendance_header_alert_image"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/app_name"
android:textSize="19sp" />
<TextView
android:id="@+id/attendance_header_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_header_day"
android:layout_marginTop="5dp"
android:maxLines="1"
android:text="@string/app_name"
android:textColor="@color/secondary_text"
android:textSize="14sp" />
<TextView
android:id="@+id/attendance_header_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/attendance_header_date"
android:layout_toRightOf="@+id/attendance_header_date"
android:layout_below="@+id/attendance_header_day"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:maxLines="1"
android:text="@string/app_name"
android:textColor="@color/secondary_text"
android:textSize="14sp" />
<TextView
android:id="@+id/attendance_header_free_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginLeft="180dp"
android:layout_marginStart="180dp"
android:gravity="end"
android:maxLines="2"
android:text="@string/attendance_no_entries"
android:textColor="@color/secondary_text"
android:textSize="16sp" />
<ImageView
android:id="@+id/attendance_header_alert_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginTop="10dp"
app:srcCompat="@drawable/ic_exclamation_24dp"
tools:ignore="contentDescription" />
</RelativeLayout>
</FrameLayout>

View File

@ -0,0 +1,78 @@
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tool="http://schemas.android.com/tools"
android:id="@+id/attendance_subItem_cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="7dp"
android:layout_marginEnd="5dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginStart="5dp"
android:foreground="?attr/selectableItemBackgroundBorderless"
card_view:cardElevation="0dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="7dp"
android:layout_marginEnd="7dp"
android:layout_marginLeft="7dp"
android:layout_marginRight="7dp"
android:layout_marginStart="7dp"
android:layout_marginTop="7dp">
<TextView
android:id="@+id/attendance_subItem_number"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:gravity="center"
android:maxLength="2"
android:text="0"
android:textSize="32sp"
tool:ignore="all"/>
<TextView
android:id="@+id/attendance_subItem_lesson"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginEnd="40dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="40dp"
android:layout_marginStart="10dp"
android:layout_toEndOf="@+id/attendance_subItem_number"
android:layout_toRightOf="@+id/attendance_subItem_number"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/app_name"
android:textSize="17sp"
tool:ignore="all"/>
<TextView
android:id="@+id/attendance_subItem_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/attendance_subItem_number"
android:layout_alignLeft="@id/attendance_subItem_lesson"
android:layout_alignStart="@id/attendance_subItem_lesson"
android:maxLines="1"
android:text="@string/grades_text"
android:textColor="@color/secondary_text"
android:textSize="12sp" />
<ImageView
android:id="@+id/attendance_subItem_alert_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginTop="10dp"
app:srcCompat="@drawable/ic_exclamation_24dp"
tool:ignore="contentDescription"/>
</RelativeLayout>
</android.support.v7.widget.CardView>

View File

@ -1,36 +1,25 @@
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_alignParentBottom="true"
tools:context="io.github.wulkanowy.ui.main.attendance.AttendanceFragment"> android:id="@+id/attendance_fragment_container">
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:gravity="center">
<ImageView <android.support.design.widget.TabLayout
android:id="@+id/wrench_under_construction" android:id="@+id/attendance_fragment_tab_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView3"
android:layout_centerHorizontal="true"
android:layout_marginTop="45dp"
android:contentDescription="@string/activity_dashboard_text"
android:minHeight="100dp"
android:minWidth="100dp"
app:srcCompat="@drawable/ic_wrench_24dp" />
<TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="47dp" app:tabMinWidth="125dp"
android:text="@string/activity_under_construction" app:tabMode="scrollable"/>
android:textSize="17sp"
android:gravity="center"
android:textAlignment="center"
android:id="@+id/textView3" />
</RelativeLayout>
<android.support.v4.view.ViewPager
android:id="@+id/attendance_fragment_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/attendance_fragment_tab_layout" />
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout>

View File

@ -0,0 +1,63 @@
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/attendance_tab_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="io.github.wulkanowy.ui.main.grades.GradesFragment">
<RelativeLayout
android:id="@+id/attendance_tab_fragment_progress_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:visibility="gone">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/attendance_tab_fragment_no_item_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<ImageView
android:id="@+id/attendance_tab_fragment_no_item_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/attendance_tab_fragment_no_item_text"
android:layout_centerHorizontal="true"
android:layout_marginTop="40dp"
android:minHeight="100dp"
android:minWidth="100dp"
android:tint="@android:color/black"
app:srcCompat="@drawable/ic_menu_attendance_24dp"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/attendance_tab_fragment_no_item_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="46dp"
android:text="@string/attendance_no_entries"
android:textAlignment="center"
android:textSize="20sp" />
</RelativeLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/attendance_tab_fragment_swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/attendance_tab_fragment_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.design.widget.CoordinatorLayout>

View File

@ -96,4 +96,22 @@
<item quantity="many">Dostałeś %1$d ocen</item> <item quantity="many">Dostałeś %1$d ocen</item>
<item quantity="other">Dostałeś %1$d ocen</item> <item quantity="other">Dostałeś %1$d ocen</item>
</plurals> </plurals>
<string name="attendance_absence_for_school_reasons">Nieobecny z przyczyn szkolnych</string>
<string name="attendance_absence_excused">Nieobecność usprawiedliwiona</string>
<string name="attendance_absence_unexcused">Nieobecność nieusprawiedliwiona</string>
<string name="attendance_exemption">Zwolniony</string>
<string name="attendance_excused_lateness">Spóźnienie usprawiedliowione</string>
<string name="attendance_unexcused_lateness">Spóźnienie nieusprawiedliwione</string>
<string name="attendance_present">Obecny</string>
<string name="attendance_dialog_description">Opis</string>
<string name="attendance_dialog_date">Data</string>
<string name="attendance_dialog_subject">Przedmiot</string>
<string name="attendance_dialog_number">Numer lekcji</string>
<string name="attendance_no_entries">Brak wpisów</string>
<plurals name="numberOfAbsences">
<item quantity="one">%1$d nieobecność</item>
<item quantity="few">%1$d nieobecności</item>
<item quantity="many">%1$d nieobecności</item>
</plurals>
</resources> </resources>

View File

@ -91,4 +91,23 @@
<item quantity="one">You received %1$d grade</item> <item quantity="one">You received %1$d grade</item>
<item quantity="other">You received %1$d grades</item> <item quantity="other">You received %1$d grades</item>
</plurals> </plurals>
<string name="attendance_absence_for_school_reasons">Absence for school reasons</string>
<string name="attendance_absence_excused">Absence excused</string>
<string name="attendance_absence_unexcused">Unexcused absence</string>
<string name="attendance_exemption">Exemption</string>
<string name="attendance_excused_lateness">Excused lateness</string>
<string name="attendance_unexcused_lateness">Unexcused lateness</string>
<string name="attendance_present">Present</string>
<string name="attendance_dialog_description">Description</string>
<string name="attendance_dialog_date">Date</string>
<string name="attendance_dialog_subject">Subject</string>
<string name="attendance_dialog_number">Lesson number</string>
<string name="attendance_no_entries">No entries</string>
<plurals name="numberOfAbsences">
<item quantity="one">%1$d absence</item>
<item quantity="few">%1$d absences</item>
<item quantity="many">%1$d absences</item>
<item quantity="other">%1$d absences</item>
</plurals>
</resources> </resources>

View File

@ -10,6 +10,7 @@ import io.github.wulkanowy.api.grades.Grade;
import io.github.wulkanowy.api.grades.Subject; import io.github.wulkanowy.api.grades.Subject;
import io.github.wulkanowy.api.generic.Day; import io.github.wulkanowy.api.generic.Day;
import io.github.wulkanowy.api.generic.Lesson; import io.github.wulkanowy.api.generic.Lesson;
import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
public class DataObjectConverterTest { public class DataObjectConverterTest {
@ -64,15 +65,15 @@ public class DataObjectConverterTest {
@Test @Test
public void lessonConversionEmptyTest() { public void lessonConversionEmptyTest() {
Assert.assertEquals(new ArrayList<>(), Assert.assertEquals(new ArrayList<>(),
DataObjectConverter.lessonsToLessonsEntities(new ArrayList<Lesson>())); DataObjectConverter.lessonsToTimetableLessonsEntities(new ArrayList<Lesson>()));
} }
@Test @Test
public void lessonConversionTest() { public void lessonConversionTest() {
List<Lesson> lessonList = new ArrayList<>(); List<Lesson> lessonList = new ArrayList<>();
lessonList.add(new Lesson().setRoom("20")); lessonList.add(new Lesson().setRoom("20"));
List<io.github.wulkanowy.data.db.dao.entities.Lesson> lessonEntityList = List<TimetableLesson> lessonEntityList =
DataObjectConverter.lessonsToLessonsEntities(lessonList); DataObjectConverter.lessonsToTimetableLessonsEntities(lessonList);
Assert.assertEquals("20", lessonEntityList.get(0).getRoom()); Assert.assertEquals("20", lessonEntityList.get(0).getRoom());
} }