diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java index 7c9fc27f..677a6de0 100644 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java @@ -4,8 +4,12 @@ import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; +import java.util.Locale; import io.github.wulkanowy.api.SnP; import io.github.wulkanowy.api.generic.Day; @@ -16,25 +20,34 @@ public class AttendanceTable { private SnP snp; - private String attendancePageUrl = "Frekwencja.mvc?data="; + private final static String ATTENDANCE_PAGE_URL = "Frekwencja.mvc?data="; public AttendanceTable(SnP snp) { this.snp = snp; } - public Week<Day> getWeekTable() throws IOException { + public Week<Day> getWeekTable() throws IOException, ParseException { return getWeekTable(""); } - public Week<Day> getWeekTable(String tick) throws IOException { - Element table = snp.getSnPPageDocument(attendancePageUrl + tick) + public Week<Day> getWeekTable(String tick) throws IOException, ParseException { + Element table = snp.getSnPPageDocument(ATTENDANCE_PAGE_URL + tick) .select(".mainContainer .presentData").first(); Elements headerCells = table.select("thead th"); List<Day> days = new ArrayList<>(); 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"); @@ -46,26 +59,29 @@ public class AttendanceTable { // fill hours in day int size = hours.size(); 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()); + + addLessonDetails(lesson, hours.get(i)); + + days.get(i - 1).setLesson(lesson); } } - String[] dayDescription = headerCells.get(1).html().split("<br>"); - return new Week<Day>() - .setStartDayDate(dayDescription[1]) + .setStartDayDate(days.get(0).getDate()) .setDays(days); } - private Lesson getNewLesson(Element cell) { - Lesson lesson = new Lesson(); + private void addLessonDetails(Lesson lesson, Element cell) { lesson.setSubject(cell.select("span").text()); if (LessonTypes.CLASS_NOT_EXIST.equals(cell.attr("class"))) { lesson.setNotExist(true); lesson.setEmpty(true); - return lesson; + return; } switch (cell.select("div").attr("class")) { @@ -95,7 +111,5 @@ public class AttendanceTable { lesson.setEmpty(true); break; } - - return lesson; } } diff --git a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java index 1420a475..40ff4f06 100644 --- a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java @@ -20,8 +20,8 @@ public class AttendanceTableTest extends StudentAndParentTestCase { @Test public void getWeekStartByDate() throws Exception { - Assert.assertEquals("31.08.2015", excellent.getWeekTable().getStartDayDate()); - Assert.assertEquals("05.09.2016", full.getWeekTable().getStartDayDate()); + Assert.assertEquals("2015-08-31", excellent.getWeekTable().getStartDayDate()); + Assert.assertEquals("2016-09-05", full.getWeekTable().getStartDayDate()); } @Test @@ -38,13 +38,13 @@ public class AttendanceTableTest extends StudentAndParentTestCase { @Test public void getDayDate() throws Exception { - Assert.assertEquals("31.08.2015", excellent.getWeekTable().getDay(0).getDate()); - Assert.assertEquals("02.09.2015", excellent.getWeekTable().getDay(2).getDate()); - Assert.assertEquals("04.09.2015", excellent.getWeekTable().getDay(4).getDate()); + Assert.assertEquals("2015-08-31", excellent.getWeekTable().getDay(0).getDate()); + Assert.assertEquals("2015-09-02", excellent.getWeekTable().getDay(2).getDate()); + Assert.assertEquals("2015-09-04", excellent.getWeekTable().getDay(4).getDate()); - Assert.assertEquals("05.09.2016", full.getWeekTable().getDay(0).getDate()); - Assert.assertEquals("07.09.2016", full.getWeekTable().getDay(2).getDate()); - Assert.assertEquals("09.09.2016", full.getWeekTable().getDay(4).getDate()); + Assert.assertEquals("2016-09-05", full.getWeekTable().getDay(0).getDate()); + Assert.assertEquals("2016-09-07", full.getWeekTable().getDay(2).getDate()); + Assert.assertEquals("2016-09-09", full.getWeekTable().getDay(4).getDate()); } @Test diff --git a/app/build.gradle b/app/build.gradle index d4374e2d..4966d9f0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -62,7 +62,7 @@ android { } greendao { - schemaVersion 20 + schemaVersion 22 generateTests = true } diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java new file mode 100644 index 00000000..492d642d --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java @@ -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; + } + +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/LessonTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java similarity index 50% rename from app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/LessonTest.java rename to app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java index c8690ff9..b215b6be 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/LessonTest.java +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java @@ -2,15 +2,18 @@ package io.github.wulkanowy.data.db.dao.entities; 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() { - super(LessonDao.class); +public class TimetableLessonTest extends AbstractDaoTestLongPk<TimetableLessonDao, TimetableLesson> { + + public TimetableLessonTest() { + super(TimetableLessonDao.class); } @Override - protected Lesson createEntity(Long key) { - Lesson entity = new Lesson(); + protected TimetableLesson createEntity(Long key) { + TimetableLesson entity = new TimetableLesson(); entity.setId(key); entity.setIsEmpty(false); entity.setIsDivisionIntoGroups(false); diff --git a/app/src/main/java/io/github/wulkanowy/data/Repository.java b/app/src/main/java/io/github/wulkanowy/data/Repository.java index 04460a07..2637b08c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/Repository.java +++ b/app/src/main/java/io/github/wulkanowy/data/Repository.java @@ -1,6 +1,5 @@ package io.github.wulkanowy.data; - import java.io.IOException; import java.text.ParseException; 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.VulcanOfflineException; 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.Grade; 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.shared.SharedPrefContract; 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.timetable.TimetableSyncContract; import io.github.wulkanowy.di.annotations.SyncGrades; @@ -38,6 +39,8 @@ public class Repository implements RepositoryContract { private final LoginSyncContract loginSync; + private final AttendanceSyncContract attendanceSync; + private final TimetableSyncContract timetableSync; private final SyncContract gradeSync; @@ -49,6 +52,7 @@ public class Repository implements RepositoryContract { ResourcesContract resources, DaoSession daoSession, LoginSyncContract loginSync, + AttendanceSyncContract attendanceSync, TimetableSyncContract timetableSync, @SyncGrades SyncContract gradeSync, @SyncSubjects SyncContract subjectSync) { @@ -56,6 +60,7 @@ public class Repository implements RepositoryContract { this.resources = resources; this.daoSession = daoSession; this.loginSync = loginSync; + this.attendanceSync = attendanceSync; this.timetableSync = timetableSync; this.gradeSync = gradeSync; this.subjectSync = subjectSync; @@ -81,6 +86,11 @@ public class Repository implements RepositoryContract { return resources.getErrorLoginMessage(e); } + @Override + public String getAttendanceLessonDescription(AttendanceLesson lesson) { + return resources.getAttendanceLessonDescription(lesson); + } + @Override public void loginUser(String email, String password, String symbol) throws NotLoggedInErrorException, AccountPermissionException, IOException, @@ -104,6 +114,16 @@ public class Repository implements RepositoryContract { 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 public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException { timetableSync.syncTimetable(); @@ -118,6 +138,7 @@ public class Repository implements RepositoryContract { public void syncAll() throws NotLoggedInErrorException, IOException, ParseException { syncSubjects(); syncGrades(); + syncAttendance(); syncTimetable(); } diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java b/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java index fcdf29ed..90aaeef8 100644 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java @@ -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.Week; 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.timetable.TimetableSyncContract; @Singleton public interface RepositoryContract extends ResourcesContract, LoginSyncContract, - TimetableSyncContract { + AttendanceSyncContract, TimetableSyncContract { long getCurrentUserId(); diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java new file mode 100644 index 00000000..9250518a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java @@ -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; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java index 99574e1d..fb3ce414 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java @@ -5,6 +5,7 @@ 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.OrderBy; import org.greenrobot.greendao.annotation.Property; import org.greenrobot.greendao.annotation.ToMany; @@ -13,7 +14,7 @@ import java.util.List; @Entity( nameInDb = "Days", active = true, - indexes ={@Index(value = "userId,weekId,date", unique = true)} + indexes = {@Index(value = "userId,weekId,date", unique = true)} ) public class Day { @@ -39,7 +40,11 @@ public class Day { private String freeDayName = ""; @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 @@ -113,12 +118,12 @@ public class Day { return this; } - public boolean isFreeDay() { - return isFreeDay; + public boolean getIsFreeDay() { + return this.isFreeDay; } - public Day setFreeDay(boolean freeDay) { - isFreeDay = freeDay; + public Day setIsFreeDay(boolean isFreeDay) { + this.isFreeDay = isFreeDay; return this; } @@ -135,30 +140,58 @@ public class Day { * 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 = 1552857303) - public List<Lesson> getLessons() { - if (lessons == null) { + @Generated(hash = 218588195) + public List<TimetableLesson> getTimetableLessons() { + if (timetableLessons == null) { final DaoSession daoSession = this.daoSession; if (daoSession == null) { throw new DaoException("Entity is detached from DAO context"); } - LessonDao targetDao = daoSession.getLessonDao(); - List<Lesson> lessonsNew = targetDao._queryDay_Lessons(id); + TimetableLessonDao targetDao = daoSession.getTimetableLessonDao(); + List<TimetableLesson> timetableLessonsNew = targetDao + ._queryDay_TimetableLessons(id); synchronized (this) { - if (lessons == null) { - lessons = lessonsNew; + if (timetableLessons == null) { + 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) - public synchronized void resetLessons() { - lessons = null; + @Generated(hash = 1166820581) + public List<AttendanceLesson> getAttendanceLessons() { + 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); } - public boolean getIsFreeDay() { - return this.isFreeDay; - } - - public void setIsFreeDay(boolean isFreeDay) { - this.isFreeDay = isFreeDay; - } - /** called by internal mechanisms, do not call yourself. */ @Generated(hash = 1409317752) public void __setDaoSession(DaoSession daoSession) { diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Lesson.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java similarity index 72% rename from app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Lesson.java rename to app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java index db9be944..390f679e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Lesson.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java @@ -10,11 +10,11 @@ import org.greenrobot.greendao.annotation.Property; import java.io.Serializable; @Entity( - nameInDb = "Lessons", + nameInDb = "TimetableLessons", active = true, indexes = {@Index(value = "dayId,date,startTime,endTime", unique = true)} ) -public class Lesson implements Serializable { +public class TimetableLesson implements Serializable { @Id(autoincrement = true) private Long id; @@ -78,14 +78,15 @@ public class Lesson implements Serializable { /** * Used for active entity operations. */ - @Generated(hash = 610143130) - private transient LessonDao myDao; + @Generated(hash = 1119360138) + private transient TimetableLessonDao myDao; - @Generated(hash = 140778287) - public Lesson(Long id, Long dayId, String number, String subject, String teacher, String room, - String description, String groupName, String startTime, String endTime, String date, - boolean isEmpty, boolean isDivisionIntoGroups, boolean isPlanning, boolean isRealized, - boolean isMovedOrCanceled, boolean isNewMovedInOrChanged) { + @Generated(hash = 627457324) + public TimetableLesson(Long id, Long dayId, String number, String subject, + String teacher, String room, String description, String groupName, + String startTime, String endTime, String date, boolean isEmpty, + boolean isDivisionIntoGroups, boolean isPlanning, boolean isRealized, + boolean isMovedOrCanceled, boolean isNewMovedInOrChanged) { this.id = id; this.dayId = dayId; this.number = number; @@ -105,8 +106,8 @@ public class Lesson implements Serializable { this.isNewMovedInOrChanged = isNewMovedInOrChanged; } - @Generated(hash = 1669664117) - public Lesson() { + @Generated(hash = 1878030142) + public TimetableLesson() { } public Long getId() { @@ -126,186 +127,138 @@ public class Lesson implements Serializable { } public String getNumber() { - return number; + return this.number; } - public Lesson setNumber(String number) { + public TimetableLesson setNumber(String number) { this.number = number; return this; } public String getSubject() { - return subject; + return this.subject; } - public Lesson setSubject(String subject) { + public TimetableLesson setSubject(String subject) { this.subject = subject; return this; } public String getTeacher() { - return teacher; + return this.teacher; } - public Lesson setTeacher(String teacher) { + public TimetableLesson setTeacher(String teacher) { this.teacher = teacher; return this; } public String getRoom() { - return room; + return this.room; } - public Lesson setRoom(String room) { + public TimetableLesson setRoom(String room) { this.room = room; return this; } public String getDescription() { - return description; + return this.description; } - public Lesson setDescription(String description) { + public TimetableLesson setDescription(String description) { this.description = description; return this; } public String getGroupName() { - return groupName; + return this.groupName; } - public Lesson setGroupName(String groupName) { + public TimetableLesson setGroupName(String groupName) { this.groupName = groupName; return this; } public String getStartTime() { - return startTime; + return this.startTime; } - public Lesson setStartTime(String startTime) { + public TimetableLesson setStartTime(String startTime) { this.startTime = startTime; return this; } public String getEndTime() { - return endTime; + return this.endTime; } - public Lesson setEndTime(String endTime) { + public TimetableLesson setEndTime(String endTime) { this.endTime = endTime; return this; } public String getDate() { - return date; + return this.date; } - public Lesson setDate(String date) { + public TimetableLesson setDate(String date) { this.date = date; 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() { return this.isEmpty; } - public void setIsEmpty(boolean isEmpty) { + public TimetableLesson setEmpty(boolean isEmpty) { this.isEmpty = isEmpty; + return this; } public boolean getIsDivisionIntoGroups() { return this.isDivisionIntoGroups; } - public void setIsDivisionIntoGroups(boolean isDivisionIntoGroups) { + public TimetableLesson setDivisionIntoGroups(boolean isDivisionIntoGroups) { this.isDivisionIntoGroups = isDivisionIntoGroups; + return this; } public boolean getIsPlanning() { return this.isPlanning; } - public void setIsPlanning(boolean isPlanning) { + public TimetableLesson setPlanning(boolean isPlanning) { this.isPlanning = isPlanning; + return this; } public boolean getIsRealized() { return this.isRealized; } - public void setIsRealized(boolean isRealized) { + public TimetableLesson setRealized(boolean isRealized) { this.isRealized = isRealized; + return this; } public boolean getIsMovedOrCanceled() { return this.isMovedOrCanceled; } - public void setIsMovedOrCanceled(boolean isMovedOrCanceled) { + public TimetableLesson setMovedOrCanceled(boolean isMovedOrCanceled) { this.isMovedOrCanceled = isMovedOrCanceled; + return this; } public boolean getIsNewMovedInOrChanged() { return this.isNewMovedInOrChanged; } - public void setIsNewMovedInOrChanged(boolean isNewMovedInOrChanged) { + public TimetableLesson setNewMovedInOrChanged(boolean isNewMovedInOrChanged) { this.isNewMovedInOrChanged = isNewMovedInOrChanged; + return this; } /** @@ -344,10 +297,34 @@ public class Lesson implements Serializable { 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. */ - @Generated(hash = 2078826279) + @Generated(hash = 1885258429) public void __setDaoSession(DaoSession daoSession) { this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getLessonDao() : null; + myDao = daoSession != null ? daoSession.getTimetableLessonDao() : null; } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java index c82f3277..778f6ca3 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java @@ -26,6 +26,12 @@ public class Week { @Property(nameInDb = "START_DATE") private String startDayDate = ""; + @Property(nameInDb = "IS_ATTENDANCE_SYNCED") + private boolean isAttendanceSynced = false; + + @Property(nameInDb = "IS_TIMETABLE_SYNCED") + private boolean isTimetableSynced = false; + @ToMany(referencedJoinProperty = "weekId") private List<Day> dayList; @@ -37,11 +43,14 @@ public class Week { @Generated(hash = 1019310398) private transient WeekDao myDao; - @Generated(hash = 36829814) - public Week(Long id, Long userId, String startDayDate) { + @Generated(hash = 1745118398) + public Week(Long id, Long userId, String startDayDate, boolean isAttendanceSynced, + boolean isTimetableSynced) { this.id = id; this.userId = userId; this.startDayDate = startDayDate; + this.isAttendanceSynced = isAttendanceSynced; + this.isTimetableSynced = isTimetableSynced; } @Generated(hash = 2135529658) @@ -75,6 +84,22 @@ public class Week { 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). * Changes to to-many relations are not persisted, make changes to the target entity. diff --git a/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java b/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java index 8e7227d3..e9b85464 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java @@ -13,6 +13,7 @@ import javax.inject.Singleton; import io.github.wulkanowy.R; import io.github.wulkanowy.api.login.NotLoggedInErrorException; 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.utils.AppConstant; import io.github.wulkanowy.utils.LogUtils; @@ -56,4 +57,35 @@ public class AppResources implements ResourcesContract { 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); + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java index 3768ad2c..0ef45a76 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java @@ -1,5 +1,7 @@ package io.github.wulkanowy.data.db.resources; +import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; + public interface ResourcesContract { String[] getSymbolsKeysArray(); @@ -7,4 +9,6 @@ public interface ResourcesContract { String[] getSymbolsValuesArray(); String getErrorLoginMessage(Exception e); + + String getAttendanceLessonDescription(AttendanceLesson lesson); } diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSync.java new file mode 100644 index 00000000..19de88ef --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSync.java @@ -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(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSyncContract.java b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSyncContract.java new file mode 100644 index 00000000..503086ba --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSyncContract.java @@ -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; +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java index dcb048f1..11819bed 100644 --- a/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java +++ b/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java @@ -9,12 +9,13 @@ 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.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.Lesson; -import io.github.wulkanowy.data.db.dao.entities.LessonDao; +import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; +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.WeekDao; import io.github.wulkanowy.data.db.shared.SharedPrefContract; @@ -27,9 +28,11 @@ public class TimetableSync implements TimetableSyncContract { private final DaoSession daoSession; + private final SharedPrefContract sharedPref; + private final Vulcan vulcan; - private final SharedPrefContract sharedPref; + private long userId; @Inject TimetableSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) { @@ -38,83 +41,122 @@ public class TimetableSync implements TimetableSyncContract { 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 public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException { 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(); + } } diff --git a/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java b/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java index 01a0fc02..b26abaaf 100644 --- a/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java +++ b/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java @@ -4,6 +4,7 @@ import dagger.Component; import io.github.wulkanowy.di.annotations.PerFragment; import io.github.wulkanowy.di.modules.FragmentModule; 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.grades.GradesFragment; import io.github.wulkanowy.ui.main.timetable.TimetableFragment; @@ -17,6 +18,8 @@ public interface FragmentComponent { void inject(AttendanceFragment attendanceFragment); + void inject(AttendanceTabFragment attendanceTabFragment); + void inject(DashboardFragment dashboardFragment); void inject(TimetableFragment timetableFragment); diff --git a/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java b/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java index 58e9909b..46744c90 100644 --- a/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java +++ b/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java @@ -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.SharedPrefContract; 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.login.LoginSync; import io.github.wulkanowy.data.sync.login.LoginSyncContract; @@ -122,6 +124,12 @@ public class ApplicationModule { return timetableSync; } + @Singleton + @Provides + AttendanceSyncContract provideAttendanceSync(AttendanceSync attendanceSync) { + return attendanceSync; + } + @Provides FirebaseJobDispatcher provideDispatcher() { return new FirebaseJobDispatcher(new GooglePlayDriver(application)); diff --git a/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java b/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java index 8999bca8..3479a8c4 100644 --- a/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java +++ b/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java @@ -7,7 +7,11 @@ import dagger.Provides; import eu.davidea.flexibleadapter.FlexibleAdapter; import io.github.wulkanowy.di.annotations.PerFragment; 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.AttendanceTabContract; +import io.github.wulkanowy.ui.main.attendance.AttendanceTabPresenter; import io.github.wulkanowy.ui.main.dashboard.DashboardContract; import io.github.wulkanowy.ui.main.dashboard.DashboardPresenter; import io.github.wulkanowy.ui.main.grades.GradeHeaderItem; @@ -47,6 +51,22 @@ public class FragmentModule { 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 @Provides TimetableContract.Presenter provideTimetablePresenter(TimetablePresenter timetablePresenter) { @@ -65,12 +85,12 @@ public class FragmentModule { } @Provides - FlexibleAdapter<GradeHeaderItem> provideGradesAdapter() { + FlexibleAdapter<TimetableHeaderItem> provideTimetableTabAdapter() { return new FlexibleAdapter<>(null); } @Provides - FlexibleAdapter<TimetableHeaderItem> provideTimetableTabAdapter() { + FlexibleAdapter<GradeHeaderItem> provideGradesAdapter() { return new FlexibleAdapter<>(null); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java index d4c0b9da..cc9b1dc8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java @@ -18,6 +18,7 @@ import butterknife.ButterKnife; import io.github.wulkanowy.R; import io.github.wulkanowy.services.SyncJob; 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.grades.GradesFragment; import io.github.wulkanowy.ui.main.timetable.TimetableFragment; @@ -134,7 +135,7 @@ public class MainActivity extends BaseActivity implements MainContract.View, private void initiationViewPager() { pagerAdapter.addFragment(new GradesFragment()); - pagerAdapter.addFragment(new DashboardFragment()); + pagerAdapter.addFragment(new AttendanceFragment()); pagerAdapter.addFragment(new DashboardFragment()); pagerAdapter.addFragment(new TimetableFragment()); pagerAdapter.addFragment(new DashboardFragment()); diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TabsData.java b/app/src/main/java/io/github/wulkanowy/ui/main/TabsData.java similarity index 64% rename from app/src/main/java/io/github/wulkanowy/ui/main/timetable/TabsData.java rename to app/src/main/java/io/github/wulkanowy/ui/main/TabsData.java index 156d1759..3af29534 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TabsData.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/TabsData.java @@ -1,35 +1,35 @@ -package io.github.wulkanowy.ui.main.timetable; +package io.github.wulkanowy.ui.main; import android.support.v4.app.Fragment; import java.util.ArrayList; import java.util.List; -class TabsData { +public class TabsData { private List<Fragment> fragments = new ArrayList<>(); private List<String> titles = new ArrayList<>(); - Fragment getFragment(int index) { + public Fragment getFragment(int index) { return fragments.get(index); } - void addFragment(Fragment fragment) { + public void addFragment(Fragment fragment) { if (fragment != null) { fragments.add(fragment); } } - int getFragmentsCount() { + public int getFragmentsCount() { return fragments.size(); } - String getTitle(int index) { + public String getTitle(int index) { return titles.get(index); } - void addTitle(String title) { + public void addTitle(String title) { if (title != null) { titles.add(title); } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java index fe403717..f8d3b302 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java @@ -1,8 +1,9 @@ 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.main.OnFragmentIsReadyListener; +import io.github.wulkanowy.ui.main.TabsData; public interface AttendanceContract { @@ -10,14 +11,26 @@ public interface AttendanceContract { void setActivityTitle(); + void scrollViewPagerToPosition(int position); + + void setTabDataToAdapter(TabsData tabsData); + + void setAdapterWithTabLayout(); + + void setChildFragmentSelected(int position, boolean selected); + boolean isMenuVisible(); } - @PerFragment + @PerActivity interface Presenter extends BaseContract.Presenter<View> { - void onStart(View view, OnFragmentIsReadyListener listener); - void onFragmentVisible(boolean isVisible); + + void onTabSelected(int position); + + void onTabUnselected(int position); + + void onStart(View view, OnFragmentIsReadyListener listener); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java new file mode 100644 index 00000000..f5bbcb41 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java @@ -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(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java index 6010d108..82bb2520 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java @@ -2,30 +2,42 @@ 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.design.widget.TabLayout; +import android.support.v4.view.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import javax.inject.Inject; +import butterknife.BindView; import butterknife.ButterKnife; import io.github.wulkanowy.R; import io.github.wulkanowy.di.component.FragmentComponent; import io.github.wulkanowy.ui.base.BaseFragment; 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 AttendanceContract.Presenter presenter; - public AttendanceFragment() { - // empty constructor for fragment - } - + @Nullable @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_attendance, container, false); 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 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 public void onDestroyView() { - super.onDestroyView(); presenter.onDestroy(); + super.onDestroyView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java new file mode 100644 index 00000000..0270f7c1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java @@ -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; + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePagerAdapter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePagerAdapter.java new file mode 100644 index 00000000..3cabff71 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePagerAdapter.java @@ -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); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java index da43e320..73dd44ca 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java @@ -1,16 +1,33 @@ 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.ui.base.BasePresenter; 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> - 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 int positionToScroll; + + private boolean isFirstSight = false; + @Inject AttendancePresenter(RepositoryContract repository) { super(repository); @@ -25,7 +42,18 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View> 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 @@ -34,4 +62,50 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View> 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(); + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java new file mode 100644 index 00000000..6da35555 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java @@ -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()); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java new file mode 100644 index 00000000..1bac005d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java @@ -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(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java new file mode 100644 index 00000000..90313daa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java @@ -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(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java new file mode 100644 index 00000000..7f4766c9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java @@ -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(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java index 18fa33f9..eaad0505 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java @@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.main.timetable; import io.github.wulkanowy.di.annotations.PerActivity; import io.github.wulkanowy.ui.base.BaseContract; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; +import io.github.wulkanowy.ui.main.TabsData; public interface TimetableContract { diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java index cfc1e073..b9390195 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java @@ -15,13 +15,13 @@ import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; 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 { private static final String ARGUMENT_KEY = "Item"; - private Lesson lesson; + private TimetableLesson lesson; @BindView(R.id.timetable_dialog_lesson_value) TextView lessonName; @@ -54,7 +54,7 @@ public class TimetableDialogFragment extends DialogFragment { //empty constructor for fragment } - public static TimetableDialogFragment newInstance(Lesson lesson) { + public static TimetableDialogFragment newInstance(TimetableLesson lesson) { TimetableDialogFragment dialogFragment = new TimetableDialogFragment(); Bundle bundle = new Bundle(); @@ -69,7 +69,7 @@ public class TimetableDialogFragment extends DialogFragment { public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { - lesson = (Lesson) getArguments().getSerializable(ARGUMENT_KEY); + lesson = (TimetableLesson) getArguments().getSerializable(ARGUMENT_KEY); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java index a9a83391..8bef4fb2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java @@ -18,6 +18,7 @@ import io.github.wulkanowy.R; import io.github.wulkanowy.di.component.FragmentComponent; import io.github.wulkanowy.ui.base.BaseFragment; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; +import io.github.wulkanowy.ui.main.TabsData; public class TimetableFragment extends BaseFragment implements TimetableContract.View, TabLayout.OnTabSelectedListener { diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java index 6393179e..39a8f012 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java @@ -94,10 +94,10 @@ public class TimetableHeaderItem dayName.setText(StringUtils.capitalize(item.getDayName())); date.setText(item.getDate()); 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()); - if (item.isFreeDay()) { + if (item.getIsFreeDay()) { ((FrameLayout) getContentView()).setForeground(null); getContentView().setBackgroundColor(backgroundFreeDay); dayName.setTextColor(secondaryColor); diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java index 24e7582d..0b852342 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java @@ -5,6 +5,8 @@ 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 TimetablePagerAdapter extends FragmentStatePagerAdapter { private TabsData tabsData; diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java index 5d17a442..e816a190 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java @@ -8,6 +8,7 @@ import javax.inject.Inject; import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.ui.base.BasePresenter; 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; diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java index b4212940..ca82f783 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java @@ -8,6 +8,9 @@ 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; @@ -16,26 +19,41 @@ 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.Lesson; +import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; public class TimetableSubItem 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); this.lesson = lesson; } - public Lesson getLesson() { + public TimetableLesson getLesson() { return lesson; } @Override 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 @@ -72,7 +90,7 @@ public class TimetableSubItem private Context context; - private Lesson item; + private TimetableLesson item; SubItemViewHolder(View view, FlexibleAdapter adapter) { super(view, adapter); @@ -81,7 +99,7 @@ public class TimetableSubItem view.setOnClickListener(this); } - void onBind(Lesson lesson) { + void onBind(TimetableLesson lesson) { item = lesson; lessonName.setText(lesson.getSubject()); diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java index 43ead53e..00dcef6a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java @@ -8,7 +8,7 @@ import javax.inject.Inject; import io.github.wulkanowy.data.RepositoryContract; 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.ui.base.BasePresenter; import io.github.wulkanowy.utils.async.AbstractTask; @@ -96,7 +96,7 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi public void onDoInBackgroundLoading() throws Exception { Week week = getRepository().getWeek(date); - if (week == null) { + if (week == null || !week.getIsTimetableSynced()) { syncData(); week = getRepository().getWeek(date); } @@ -111,14 +111,14 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi TimetableHeaderItem headerItem = new TimetableHeaderItem(day); if (isFreeWeek) { - isFreeWeek = day.isFreeDay(); + isFreeWeek = day.getIsFreeDay(); } - List<Lesson> lessonList = day.getLessons(); + List<TimetableLesson> lessonList = day.getTimetableLessons(); List<TimetableSubItem> subItems = new ArrayList<>(); - for (Lesson lesson : lessonList) { + for (TimetableLesson lesson : lessonList) { subItems.add(new TimetableSubItem(headerItem, lesson)); } @@ -143,8 +143,10 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi if (headerItems.isEmpty()) { getView().showNoItem(true); getView().setFreeWeekName(freeWeekName); + getView().updateAdapterList(null); } else { getView().updateAdapterList(headerItems); + getView().showNoItem(false); } getView().showProgressBar(false); } diff --git a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java index 1dd5f641..a22e4d8b 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java +++ b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java @@ -4,10 +4,11 @@ package io.github.wulkanowy.utils; import java.util.ArrayList; 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.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.TimetableLesson; import io.github.wulkanowy.data.db.dao.entities.Week; public final class DataObjectConverter { @@ -60,7 +61,7 @@ public final class DataObjectConverter { return new Day() .setDate(day.getDate()) .setDayName(day.getDayName()) - .setFreeDay(day.isFreeDay()) + .setIsFreeDay(day.isFreeDay()) .setFreeDayName(day.getFreeDayName()); } @@ -75,8 +76,8 @@ public final class DataObjectConverter { return dayEntityList; } - public static Lesson lessonToLessonEntity(io.github.wulkanowy.api.generic.Lesson lesson) { - return new Lesson() + public static TimetableLesson lessonToTimetableLessonEntity(io.github.wulkanowy.api.generic.Lesson lesson) { + return new TimetableLesson() .setNumber(lesson.getNumber()) .setSubject(lesson.getSubject()) .setTeacher(lesson.getTeacher()) @@ -94,12 +95,36 @@ public final class DataObjectConverter { .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) { - 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; } diff --git a/app/src/main/res/layout/attendance_dialog.xml b/app/src/main/res/layout/attendance_dialog.xml new file mode 100644 index 00000000..916c993c --- /dev/null +++ b/app/src/main/res/layout/attendance_dialog.xml @@ -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> diff --git a/app/src/main/res/layout/attendance_header.xml b/app/src/main/res/layout/attendance_header.xml new file mode 100644 index 00000000..61dac631 --- /dev/null +++ b/app/src/main/res/layout/attendance_header.xml @@ -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> diff --git a/app/src/main/res/layout/attendance_subitem.xml b/app/src/main/res/layout/attendance_subitem.xml new file mode 100644 index 00000000..9509333f --- /dev/null +++ b/app/src/main/res/layout/attendance_subitem.xml @@ -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> diff --git a/app/src/main/res/layout/fragment_attendance.xml b/app/src/main/res/layout/fragment_attendance.xml index 6df75c8e..a1d578ab 100644 --- a/app/src/main/res/layout/fragment_attendance.xml +++ b/app/src/main/res/layout/fragment_attendance.xml @@ -1,36 +1,25 @@ <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_height="match_parent" - xmlns:app="http://schemas.android.com/apk/res-auto" - tools:context="io.github.wulkanowy.ui.main.attendance.AttendanceFragment"> + android:layout_alignParentBottom="true" + android:id="@+id/attendance_fragment_container"> <RelativeLayout android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="center"> + android:layout_height="match_parent"> - <ImageView - android:id="@+id/wrench_under_construction" - 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.support.design.widget.TabLayout + android:id="@+id/attendance_fragment_tab_layout" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="47dp" - android:text="@string/activity_under_construction" - android:textSize="17sp" - android:gravity="center" - android:textAlignment="center" - android:id="@+id/textView3" /> - </RelativeLayout> + app:tabMinWidth="125dp" + app:tabMode="scrollable"/> + <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> diff --git a/app/src/main/res/layout/fragment_attendance_tab.xml b/app/src/main/res/layout/fragment_attendance_tab.xml new file mode 100644 index 00000000..d859102d --- /dev/null +++ b/app/src/main/res/layout/fragment_attendance_tab.xml @@ -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> diff --git a/app/src/main/res/layout/fragment_timetable.xml b/app/src/main/res/layout/fragment_timetable.xml index d16230c1..f78f1ab2 100644 --- a/app/src/main/res/layout/fragment_timetable.xml +++ b/app/src/main/res/layout/fragment_timetable.xml @@ -22,4 +22,4 @@ android:layout_height="match_parent" android:layout_below="@id/timetable_fragment_tab_layout" /> </RelativeLayout> -</android.support.design.widget.CoordinatorLayout> \ No newline at end of file +</android.support.design.widget.CoordinatorLayout> diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 8fbe68fb..9642a53d 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -96,4 +96,22 @@ <item quantity="many">Dostałeś %1$d ocen</item> <item quantity="other">Dostałeś %1$d ocen</item> </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> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 56f4ffba..315fee4f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -91,4 +91,23 @@ <item quantity="one">You received %1$d grade</item> <item quantity="other">You received %1$d grades</item> </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> diff --git a/app/src/test/java/io/github/wulkanowy/utils/DataObjectConverterTest.java b/app/src/test/java/io/github/wulkanowy/utils/DataObjectConverterTest.java index 8899ed33..f32877c5 100644 --- a/app/src/test/java/io/github/wulkanowy/utils/DataObjectConverterTest.java +++ b/app/src/test/java/io/github/wulkanowy/utils/DataObjectConverterTest.java @@ -10,6 +10,7 @@ import io.github.wulkanowy.api.grades.Grade; import io.github.wulkanowy.api.grades.Subject; import io.github.wulkanowy.api.generic.Day; import io.github.wulkanowy.api.generic.Lesson; +import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; public class DataObjectConverterTest { @@ -64,15 +65,15 @@ public class DataObjectConverterTest { @Test public void lessonConversionEmptyTest() { Assert.assertEquals(new ArrayList<>(), - DataObjectConverter.lessonsToLessonsEntities(new ArrayList<Lesson>())); + DataObjectConverter.lessonsToTimetableLessonsEntities(new ArrayList<Lesson>())); } @Test public void lessonConversionTest() { List<Lesson> lessonList = new ArrayList<>(); lessonList.add(new Lesson().setRoom("20")); - List<io.github.wulkanowy.data.db.dao.entities.Lesson> lessonEntityList = - DataObjectConverter.lessonsToLessonsEntities(lessonList); + List<TimetableLesson> lessonEntityList = + DataObjectConverter.lessonsToTimetableLessonsEntities(lessonList); Assert.assertEquals("20", lessonEntityList.get(0).getRoom()); }