Add attendance (#58)

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

View File

@ -4,8 +4,12 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import 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;
}
}

View File

@ -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

View File

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

View File

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

View File

@ -2,15 +2,18 @@ package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.test.AbstractDaoTestLongPk;
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);

View File

@ -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();
}

View File

@ -11,12 +11,13 @@ import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.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();

View File

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

View File

@ -5,6 +5,7 @@ import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.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) {

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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);
}

View File

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

View File

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

View File

@ -9,12 +9,13 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import 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();
}
}

View File

@ -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);

View File

@ -21,6 +21,8 @@ import io.github.wulkanowy.data.db.resources.ResourcesContract;
import io.github.wulkanowy.data.db.shared.SharedPref;
import io.github.wulkanowy.data.db.shared.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));

View File

@ -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);
}
}

View File

@ -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());

View File

@ -1,35 +1,35 @@
package io.github.wulkanowy.ui.main.timetable;
package io.github.wulkanowy.ui.main;
import android.support.v4.app.Fragment;
import 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);
}

View File

@ -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);
}
}

View File

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

View File

@ -2,30 +2,42 @@ package io.github.wulkanowy.ui.main.attendance;
import android.os.Bundle;
import android.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();
}
}

View File

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

View File

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

View File

@ -1,16 +1,33 @@
package io.github.wulkanowy.ui.main.attendance;
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();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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());

View File

@ -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);
}

View File

@ -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;
}

View File

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

View File

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

View File

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

View File

@ -1,36 +1,25 @@
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
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>

View File

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

View File

@ -22,4 +22,4 @@
android:layout_height="match_parent"
android:layout_below="@id/timetable_fragment_tab_layout" />
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
</android.support.design.widget.CoordinatorLayout>

View File

@ -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>

View File

@ -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>

View File

@ -10,6 +10,7 @@ import io.github.wulkanowy.api.grades.Grade;
import io.github.wulkanowy.api.grades.Subject;
import io.github.wulkanowy.api.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());
}