diff --git a/api/build.gradle b/api/build.gradle index 001d2579..1f7cf197 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'java-library' +apply plugin: 'kotlin' apply plugin: 'org.sonarqube' apply plugin: 'jacoco' apply plugin: 'com.jfrog.bintray' @@ -31,6 +32,9 @@ dependencies { implementation "org.jsoup:jsoup:$jsoup" implementation "org.apache.commons:commons-lang3:$apacheLang" implementation "com.google.code.gson:gson:$gson" + implementation "org.slf4j:slf4j-api:$slf4jApi" + + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" testImplementation "junit:junit:$junit" testImplementation "org.mockito:mockito-core:$mockito" @@ -115,3 +119,29 @@ artifacts { archives sourcesJar archives javadocJar } + +buildscript { + ext.kotlin_version = '1.2.41' + repositories { + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +repositories { + mavenCentral() +} + +compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} + +compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/Client.java b/api/src/main/java/io/github/wulkanowy/api/Client.java index 277b8039..8bbae7ca 100644 --- a/api/src/main/java/io/github/wulkanowy/api/Client.java +++ b/api/src/main/java/io/github/wulkanowy/api/Client.java @@ -3,6 +3,8 @@ package io.github.wulkanowy.api; import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Date; @@ -27,6 +29,8 @@ public class Client { private Cookies cookies = new Cookies(); + private static final Logger logger = LoggerFactory.getLogger(Client.class); + Client(String email, String password, String symbol) { this.email = email; this.password = password; @@ -58,10 +62,15 @@ public class Client { return; } + clearCookies(); this.symbol = new Login(this).login(email, password, symbol); + logger.info("Login successful on {} at {}", getHost(), new Date()); } private boolean isLoggedIn() { + logger.debug("Last success request: {}", lastSuccessRequest); + logger.debug("Cookies: {}", getCookies().size()); + return getCookies().size() > 0 && lastSuccessRequest != null && 5 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime()); @@ -75,14 +84,14 @@ public class Client { this.symbol = symbol; } - public void addCookies(Map items) { - cookies.addItems(items); - } - private Map getCookies() { return cookies.getItems(); } + public void clearCookies() { + cookies = new Cookies(); + } + String getHost() { return host; } @@ -111,7 +120,11 @@ public class Client { this.cookies.addItems(cookies); } - Connection.Response response = Jsoup.connect(getFilledUrl(url)) + url = getFilledUrl(url); + + logger.debug("GET {}", url); + + Connection.Response response = Jsoup.connect(url) .followRedirects(true) .cookies(getCookies()) .execute(); @@ -128,7 +141,11 @@ public class Client { } public synchronized Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException { - Connection connection = Jsoup.connect(getFilledUrl(url)); + url = getFilledUrl(url); + + logger.debug("POST {}", url); + + Connection connection = Jsoup.connect(url); for (String[] data : params) { connection.data(data[0], data[1]); @@ -150,7 +167,11 @@ public class Client { public String getJsonStringByUrl(String url) throws IOException, VulcanException { login(); - Connection.Response response = Jsoup.connect(getFilledUrl(url)) + url = getFilledUrl(url); + + logger.debug("GET {}", url); + + Connection.Response response = Jsoup.connect(url) .followRedirects(true) .ignoreContentType(true) .cookies(getCookies()) @@ -164,7 +185,11 @@ public class Client { public String postJsonStringByUrl(String url, String[][] params) throws IOException, VulcanException { login(); - Connection connection = Jsoup.connect(getFilledUrl(url)); + url = getFilledUrl(url); + + logger.debug("POST {}", url); + + Connection connection = Jsoup.connect(url); for (String[] data : params) { connection.data(data[0], data[1]); @@ -196,7 +221,7 @@ public class Client { } if ("Błąd strony".equals(title)) { - throw new NotLoggedInErrorException(title + " " + doc.selectFirst("p, body") + ", status: " + code); + throw new NotLoggedInErrorException(title + " " + doc.body() + ", status: " + code); } return doc; diff --git a/api/src/main/java/io/github/wulkanowy/api/DateTimeUtils.kt b/api/src/main/java/io/github/wulkanowy/api/DateTimeUtils.kt new file mode 100644 index 00000000..3b620db5 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/DateTimeUtils.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.api + +import java.text.SimpleDateFormat +import java.util.* + +const val LOG_DATE_PATTERN = "dd.MM.yyyy" +const val API_DATE_PATTERN = "yyyy-MM-dd" + +const val TICKS_AT_EPOCH = 621355968000000000L +const val TICKS_PER_MILLISECOND = 10000 + +fun getFormattedDate(date: String): String { + return getFormattedDate(date, API_DATE_PATTERN) +} + +fun getFormattedDate(date: String, format: String): String { + return getFormattedDate(date, LOG_DATE_PATTERN, format) +} + +fun getFormattedDate(date: String, fromFormat: String, toFormat: String): String { + val sdf = SimpleDateFormat(fromFormat, Locale.ROOT) + val d = sdf.parse(date) + sdf.applyPattern(toFormat) + + return sdf.format(d) +} + +fun getDateAsTick(dateString: String?): String { + if (dateString.isNullOrEmpty()) { + return "" + } + + return getDateAsTick(dateString as String, API_DATE_PATTERN).toString() +} + +fun getDateAsTick(dateString: String, dateFormat: String): Long { + val format = SimpleDateFormat(dateFormat, Locale.ROOT) + format.timeZone = TimeZone.getTimeZone("UTC") + val dateObject = format.parse(dateString) + + return getDateAsTick(dateObject) +} + +fun getDateAsTick(date: Date): Long { + val calendar = Calendar.getInstance() + calendar.time = date + + return calendar.timeInMillis * TICKS_PER_MILLISECOND + TICKS_AT_EPOCH +} + +fun getDate(netTicks: Long): Date { + return Date((netTicks - TICKS_AT_EPOCH) / TICKS_PER_MILLISECOND) +} diff --git a/api/src/main/java/io/github/wulkanowy/api/SnP.java b/api/src/main/java/io/github/wulkanowy/api/SnP.java index 7f074071..6747faf0 100644 --- a/api/src/main/java/io/github/wulkanowy/api/SnP.java +++ b/api/src/main/java/io/github/wulkanowy/api/SnP.java @@ -6,6 +6,11 @@ import org.jsoup.nodes.Element; import java.io.IOException; import java.util.List; +import io.github.wulkanowy.api.generic.Diary; +import io.github.wulkanowy.api.generic.ParamItem; +import io.github.wulkanowy.api.generic.Semester; +import io.github.wulkanowy.api.generic.Student; + public interface SnP { String getSchoolID(); diff --git a/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java b/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java index 16ee002e..03a7b0e5 100644 --- a/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java +++ b/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java @@ -3,6 +3,8 @@ package io.github.wulkanowy.api; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.URL; @@ -11,6 +13,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import io.github.wulkanowy.api.generic.Diary; +import io.github.wulkanowy.api.generic.ParamItem; +import io.github.wulkanowy.api.generic.Semester; +import io.github.wulkanowy.api.generic.Student; + public class StudentAndParent implements SnP { private static final String START_PAGE_URL = "{schema}://uonetplus.{host}/{symbol}/Start.mvc/Index"; @@ -27,6 +34,8 @@ public class StudentAndParent implements SnP { private String diaryID; + private static final Logger logger = LoggerFactory.getLogger(StudentAndParent.class); + StudentAndParent(Client client, String schoolID, String studentID, String diaryID) { this.client = client; this.schoolID = schoolID; @@ -38,6 +47,11 @@ public class StudentAndParent implements SnP { if (null == getStudentID() || "".equals(getStudentID())) { Document doc = client.getPageByUrl(getSnpHomePageUrl()); + if (doc.select("#idSection").isEmpty()) { + logger.error("Expected SnP page, got page with title: {} {}", doc.title(), doc.selectFirst("body")); + throw new VulcanException("Nieznany błąd podczas pobierania danych. Strona: " + doc.title()); + } + Student student = getCurrent(getStudents(doc)); studentID = student.getId(); @@ -67,24 +81,27 @@ public class StudentAndParent implements SnP { // get url to uonetplus-opiekun.fakelog.cf Document startPage = client.getPageByUrl(START_PAGE_URL); - Element studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a").first(); + Elements studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a"); - if (null == studentTileLink) { + logger.debug("studentTileLink: {}", studentTileLink.size()); + + if (studentTileLink.isEmpty()) { throw new VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?"); } - String snpPageUrl = studentTileLink.attr("href"); + String snpPageUrl = studentTileLink.last().attr("href"); this.schoolID = getExtractedIdFromUrl(snpPageUrl); return snpPageUrl; } - String getExtractedIdFromUrl(String snpPageUrl) throws NotLoggedInErrorException { + String getExtractedIdFromUrl(String snpPageUrl) throws VulcanException { String[] path = snpPageUrl.split(client.getHost())[1].split("/"); if (5 != path.length) { - throw new NotLoggedInErrorException("You are probably not logged in " + snpPageUrl); + logger.error("Expected snp url, got {}", snpPageUrl); + throw new VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?"); } return path[2]; @@ -102,12 +119,12 @@ public class StudentAndParent implements SnP { Map cookies = new HashMap<>(); cookies.put("idBiezacyDziennik", diaryID); cookies.put("idBiezacyUczen", studentID); - client.addCookies(cookies); Document doc = client.getPageByUrl(getBaseUrl() + url, true, cookies); if (!doc.title().startsWith("Witryna ucznia i rodzica")) { - throw new VulcanException("Expected SnP page, got page with title: " + doc.title()); + logger.error("Expected SnP page, got page with title: {} {}", doc.title(), doc.selectFirst("body")); + throw new VulcanException("Nieznany błąd podczas pobierania danych. Strona: " + doc.title()); } if (doc.title().endsWith("Strona główna")) { diff --git a/api/src/main/java/io/github/wulkanowy/api/Vulcan.java b/api/src/main/java/io/github/wulkanowy/api/Vulcan.java index d90d3874..2b4d1205 100644 --- a/api/src/main/java/io/github/wulkanowy/api/Vulcan.java +++ b/api/src/main/java/io/github/wulkanowy/api/Vulcan.java @@ -1,5 +1,8 @@ package io.github.wulkanowy.api; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import io.github.wulkanowy.api.attendance.AttendanceStatistics; @@ -8,6 +11,8 @@ import io.github.wulkanowy.api.exams.ExamsWeek; import io.github.wulkanowy.api.grades.GradesList; import io.github.wulkanowy.api.grades.SubjectsList; import io.github.wulkanowy.api.messages.Messages; +import io.github.wulkanowy.api.mobile.RegisterDevice; +import io.github.wulkanowy.api.mobile.RegisteredDevices; import io.github.wulkanowy.api.notes.AchievementsList; import io.github.wulkanowy.api.notes.NotesList; import io.github.wulkanowy.api.school.SchoolInfo; @@ -28,17 +33,21 @@ public class Vulcan { private String diaryId; + private static final Logger logger = LoggerFactory.getLogger(Vulcan.class); + public void setCredentials(String email, String password, String symbol, String schoolId, String studentId, String diaryId) { this.schoolId = schoolId; this.studentId = studentId; this.diaryId = diaryId; client = new Client(email, password, symbol); + + logger.debug("Client created with symbol " + symbol); } public Client getClient() throws NotLoggedInErrorException { if (null == client) { - throw new NotLoggedInErrorException("Use setCredentials() method first"); + throw new NotLoggedInErrorException("Vulcan must be initialized by calling setCredentials() prior to fetch data"); } return client; @@ -108,6 +117,14 @@ public class Vulcan { return new FamilyInformation(getStudentAndParent()); } + public RegisteredDevices getRegisteredDevices() throws VulcanException, IOException { + return new RegisteredDevices(getStudentAndParent()); + } + + public RegisterDevice getRegisterDevice() throws VulcanException, IOException { + return new RegisterDevice(getStudentAndParent()); + } + public Messages getMessages() throws VulcanException { return new Messages(getClient()); } diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java index d81a30b2..7caa70d8 100644 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java @@ -4,12 +4,8 @@ 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.VulcanException; @@ -17,6 +13,9 @@ import io.github.wulkanowy.api.generic.Day; import io.github.wulkanowy.api.generic.Lesson; import io.github.wulkanowy.api.generic.Week; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getDateAsTick; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; + public class AttendanceTable { private final static String ATTENDANCE_PAGE_URL = "Frekwencja.mvc?data="; @@ -27,13 +26,12 @@ public class AttendanceTable { this.snp = snp; } - public Week getWeekTable() throws IOException, ParseException, VulcanException { + public Week getWeekTable() throws IOException, VulcanException { return getWeekTable(""); } - public Week getWeekTable(String tick) throws IOException, ParseException, VulcanException { - Element table = snp.getSnPPageDocument(ATTENDANCE_PAGE_URL + tick) - + public Week getWeekTable(String date) throws IOException, VulcanException { + Element table = snp.getSnPPageDocument(ATTENDANCE_PAGE_URL + getDateAsTick(date)) .select(".mainContainer .presentData").first(); Elements headerCells = table.select("thead th"); @@ -42,14 +40,10 @@ public class AttendanceTable { for (int i = 1; i < headerCells.size(); i++) { String[] dayHeaderCell = headerCells.get(i).html().split("
"); - 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); + days.add(new Day() + .setDayName(dayHeaderCell[0]) + .setDate(getFormattedDate(dayHeaderCell[1].trim())) + ); } Elements hoursInDays = table.select("tbody tr"); diff --git a/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java b/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java index c111ce84..391056ac 100644 --- a/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java +++ b/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java @@ -6,17 +6,16 @@ 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.VulcanException; import io.github.wulkanowy.api.generic.Week; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getDateAsTick; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; + public class ExamsWeek { private static final String EXAMS_PAGE_URL = "Sprawdziany.mvc/Terminarz?rodzajWidoku=2&data="; @@ -27,12 +26,12 @@ public class ExamsWeek { this.snp = snp; } - public Week getCurrent() throws IOException, VulcanException, ParseException { + public Week getCurrent() throws IOException, VulcanException { return getWeek("", true); } - public Week getWeek(String tick, final boolean onlyNotEmpty) throws IOException, VulcanException, ParseException { - Document examsPage = snp.getSnPPageDocument(EXAMS_PAGE_URL + tick); + public Week getWeek(String date, final boolean onlyNotEmpty) throws IOException, VulcanException { + Document examsPage = snp.getSnPPageDocument(EXAMS_PAGE_URL + getDateAsTick(date)); Elements examsDays = examsPage.select(".mainContainer > div:not(.navigation)"); List days = new ArrayList<>(); @@ -71,11 +70,4 @@ public class ExamsWeek { .first().text().split(" ")[1])) .setDays(days); } - - private String getFormattedDate(String date) throws ParseException { - SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT); - Date d = sdf.parse(date); - sdf.applyPattern("yyyy-MM-dd"); - return sdf.format(d); - } } diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Day.java b/api/src/main/java/io/github/wulkanowy/api/generic/Day.java index 5b9086a3..fbb5ee0a 100644 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Day.java +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Day.java @@ -37,7 +37,8 @@ public class Day { return dayName; } - public void setDayName(String dayName) { + public Day setDayName(String dayName) { this.dayName = dayName; + return this; } } diff --git a/api/src/main/java/io/github/wulkanowy/api/Diary.java b/api/src/main/java/io/github/wulkanowy/api/generic/Diary.java similarity index 93% rename from api/src/main/java/io/github/wulkanowy/api/Diary.java rename to api/src/main/java/io/github/wulkanowy/api/generic/Diary.java index d7d09f9c..5c7c8593 100644 --- a/api/src/main/java/io/github/wulkanowy/api/Diary.java +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Diary.java @@ -1,4 +1,4 @@ -package io.github.wulkanowy.api; +package io.github.wulkanowy.api.generic; public class Diary implements ParamItem { diff --git a/api/src/main/java/io/github/wulkanowy/api/ParamItem.java b/api/src/main/java/io/github/wulkanowy/api/generic/ParamItem.java similarity index 67% rename from api/src/main/java/io/github/wulkanowy/api/ParamItem.java rename to api/src/main/java/io/github/wulkanowy/api/generic/ParamItem.java index c830467a..e7edfbf4 100644 --- a/api/src/main/java/io/github/wulkanowy/api/ParamItem.java +++ b/api/src/main/java/io/github/wulkanowy/api/generic/ParamItem.java @@ -1,6 +1,6 @@ -package io.github.wulkanowy.api; +package io.github.wulkanowy.api.generic; -interface ParamItem { +public interface ParamItem { ParamItem setId(String id); diff --git a/api/src/main/java/io/github/wulkanowy/api/Semester.java b/api/src/main/java/io/github/wulkanowy/api/generic/Semester.java similarity index 93% rename from api/src/main/java/io/github/wulkanowy/api/Semester.java rename to api/src/main/java/io/github/wulkanowy/api/generic/Semester.java index 31230f01..db4a724d 100644 --- a/api/src/main/java/io/github/wulkanowy/api/Semester.java +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Semester.java @@ -1,4 +1,4 @@ -package io.github.wulkanowy.api; +package io.github.wulkanowy.api.generic; public class Semester implements ParamItem { diff --git a/api/src/main/java/io/github/wulkanowy/api/Student.java b/api/src/main/java/io/github/wulkanowy/api/generic/Student.java similarity index 93% rename from api/src/main/java/io/github/wulkanowy/api/Student.java rename to api/src/main/java/io/github/wulkanowy/api/generic/Student.java index 8b22f1d8..4ed5dd37 100644 --- a/api/src/main/java/io/github/wulkanowy/api/Student.java +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Student.java @@ -1,4 +1,4 @@ -package io.github.wulkanowy.api; +package io.github.wulkanowy.api.generic; public class Student implements ParamItem { diff --git a/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java b/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java index 936c1533..cb9fe465 100644 --- a/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java +++ b/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java @@ -5,42 +5,32 @@ 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 java.util.regex.Matcher; import java.util.regex.Pattern; import io.github.wulkanowy.api.SnP; import io.github.wulkanowy.api.VulcanException; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; + public class GradesList { private static final String GRADES_PAGE_URL = "Oceny/Wszystkie?details=2&okres="; private SnP snp; - private List grades = new ArrayList<>(); - public GradesList(SnP snp) { this.snp = snp; } - private String getGradesPageUrl() { - return GRADES_PAGE_URL; - } - - public List getAll() throws IOException, ParseException, VulcanException { - return getAll(""); - } - - public List getAll(String semester) throws IOException, ParseException, VulcanException { - Document gradesPage = snp.getSnPPageDocument(getGradesPageUrl() + semester); + public List getAll(String semester) throws IOException, VulcanException { + Document gradesPage = snp.getSnPPageDocument(GRADES_PAGE_URL + semester); Elements gradesRows = gradesPage.select(".ocenySzczegoly-table > tbody > tr"); + List grades = new ArrayList<>(); + for (Element row : gradesRows) { if ("Brak ocen".equals(row.select("td:nth-child(2)").text())) { continue; @@ -52,13 +42,13 @@ public class GradesList { return grades; } - private Grade getGrade(Element row) throws ParseException { + private Grade getGrade(Element row) { String descriptions = row.select("td:nth-child(3)").text(); String symbol = descriptions.split(", ")[0]; String description = descriptions.replaceFirst(Pattern.quote(symbol), "").replaceFirst(", ", ""); String color = getColor(row.select("td:nth-child(2) span.ocenaCzastkowa").attr("style")); - String date = formatDate(row.select("td:nth-child(5)").text()); + String date = getFormattedDate(row.select("td:nth-child(5)").text()); return new Grade() .setSubject(row.select("td:nth-child(1)").text()) @@ -82,12 +72,4 @@ public class GradesList { return color; } - - private String formatDate(String date) throws ParseException { - SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT); - Date d = sdf.parse(date); - sdf.applyPattern("yyyy-MM-dd"); - - return sdf.format(d); - } } diff --git a/api/src/main/java/io/github/wulkanowy/api/login/Login.java b/api/src/main/java/io/github/wulkanowy/api/login/Login.java index 04e1efc1..f6d4cd11 100644 --- a/api/src/main/java/io/github/wulkanowy/api/login/Login.java +++ b/api/src/main/java/io/github/wulkanowy/api/login/Login.java @@ -5,6 +5,8 @@ import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.parser.Parser; import org.jsoup.select.Elements; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; @@ -21,6 +23,8 @@ public class Login { private Client client; + private static final Logger logger = LoggerFactory.getLogger(Login.class); + public Login(Client client) { this.client = client; } @@ -29,7 +33,8 @@ public class Login { Document certDoc = sendCredentials(email, password); if ("Błąd".equals(certDoc.title())) { - throw new NotLoggedInErrorException(certDoc.selectFirst("body").text()); + client.clearCookies(); + throw new NotLoggedInErrorException(certDoc.body().text()); } return sendCertificate(certDoc, symbol); @@ -88,6 +93,7 @@ public class Login { String title = targetDoc.title(); if ("Working...".equals(title)) { // on adfs login + logger.info("ADFS login"); title = sendCertData(targetDoc).title(); } @@ -96,6 +102,7 @@ public class Login { } if (!"Uonet+".equals(title)) { + logger.debug("Login failed. Body: {}", targetDoc.body()); throw new LoginErrorException("Expected page title `UONET+`, got " + title); } diff --git a/api/src/main/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt b/api/src/main/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt new file mode 100644 index 00000000..f0da01e9 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.api.mobile + +import io.github.wulkanowy.api.SnP +import org.jsoup.nodes.Element + +class RegisterDevice(private val snp: SnP) { + + companion object { + const val REGISTER_URL = "DostepMobilny.mvc/Rejestruj" + } + + data class Token( + val token: String, + val symbol: String, + val pin: String + ) + + fun getToken(): Token { + val form = snp.getSnPPageDocument(REGISTER_URL).selectFirst("#rejestracja-formularz") + + val fields = form.select(".blockElement") + + return Token( + getValue(fields[1]), + getValue(fields[2]), + getValue(fields[3]) + ) + } + + fun getValue(e: Element): String { + return e.text().split(":")[1].trim() + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/mobile/RegisteredDevices.kt b/api/src/main/java/io/github/wulkanowy/api/mobile/RegisteredDevices.kt new file mode 100644 index 00000000..d622b4de --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/mobile/RegisteredDevices.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.api.mobile + +import io.github.wulkanowy.api.SnP +import io.github.wulkanowy.api.getFormattedDate + +class RegisteredDevices(private val snp: SnP) { + + companion object { + const val DEVICES_LIST_URL = "DostepMobilny.mvc" + } + + data class Device( + val name: String, + val system: String, + val date: String, + val id: Int + ) + + fun getList(): List { + val items = snp.getSnPPageDocument(DEVICES_LIST_URL).select("table tbody tr") + val devices: MutableList = mutableListOf() + + for (item in items) { + val cells = item.select("td") + val system = cells[0].text().split("(").last().removeSuffix(")") + + devices.add(Device( + cells[0].text().replace(" ($system)", ""), + system, + getFormattedDate(cells[1].text(), "dd.MM.yyyy 'godz:' HH:mm:ss", "yyyy-MM-dd HH:mm:ss"), + cells[2].select("a").attr("href") + .split("/").last().toInt() + )) + } + + return devices + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java b/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java index 11d01342..346df832 100644 --- a/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java +++ b/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java @@ -14,9 +14,7 @@ public class AchievementsList { private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie"; - private SnP snp = null; - - private List achievements = new ArrayList<>(); + private SnP snp; public AchievementsList(SnP snp) { this.snp = snp; @@ -27,6 +25,8 @@ public class AchievementsList { .select(".mainContainer > div").get(1); Elements items = pageFragment.select("article"); + List achievements = new ArrayList<>(); + for (Element item : items) { achievements.add(item.text()); } diff --git a/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java b/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java index 0c8a30b6..54f57edf 100644 --- a/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java +++ b/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java @@ -10,13 +10,13 @@ import java.util.List; import io.github.wulkanowy.api.SnP; import io.github.wulkanowy.api.VulcanException; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; + public class NotesList { private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie"; - private SnP snp = null; - - private List notes = new ArrayList<>(); + private SnP snp; public NotesList(SnP snp) { this.snp = snp; @@ -28,10 +28,12 @@ public class NotesList { Elements items = pageFragment.select("article"); Elements dates = pageFragment.select("h2"); + List notes = new ArrayList<>(); + int index = 0; for (Element item : items) { notes.add(new Note() - .setDate(dates.get(index++).text()) + .setDate(getFormattedDate(dates.get(index++).text())) .setTeacher(snp.getRowDataChildValue(item, 1)) .setCategory(snp.getRowDataChildValue(item, 2)) .setContent(snp.getRowDataChildValue(item, 3)) diff --git a/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java b/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java index 51a7278b..f0dcd43d 100644 --- a/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java +++ b/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java @@ -11,7 +11,7 @@ public class SchoolInfo { private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele"; - private SnP snp = null; + private SnP snp; public SchoolInfo(SnP snp) { this.snp = snp; diff --git a/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java b/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java index bbf5f5d7..ec8429bd 100644 --- a/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java +++ b/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java @@ -15,7 +15,7 @@ public class TeachersInfo { private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele"; - private SnP snp = null; + private SnP snp; public TeachersInfo(SnP snp) { this.snp = snp; diff --git a/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java b/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java index 1373aee9..95c40167 100644 --- a/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java +++ b/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java @@ -3,36 +3,39 @@ package io.github.wulkanowy.api.timetable; import org.apache.commons.lang3.StringUtils; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; 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.VulcanException; import io.github.wulkanowy.api.generic.Lesson; import io.github.wulkanowy.api.generic.Week; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getDateAsTick; +import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; + public class Timetable { - private static final String TIMETABLE_PAGE_URL = "Lekcja.mvc/PlanLekcji?data="; + private static final String TIMETABLE_PAGE_URL = "Lekcja.mvc/PlanZajec?data="; private SnP snp; + private static final Logger logger = LoggerFactory.getLogger(Timetable.class); + public Timetable(SnP snp) { this.snp = snp; } - public Week getWeekTable() throws IOException, ParseException, VulcanException { + public Week getWeekTable() throws IOException, VulcanException { return getWeekTable(""); } - public Week getWeekTable(final String tick) throws IOException, ParseException, VulcanException { - Element table = snp.getSnPPageDocument(TIMETABLE_PAGE_URL + tick) + public Week getWeekTable(final String date) throws IOException, VulcanException { + Element table = snp.getSnPPageDocument(TIMETABLE_PAGE_URL + getDateAsTick(date)) .select(".mainContainer .presentData").first(); List days = getDays(table.select("thead th")); @@ -44,19 +47,20 @@ public class Timetable { .setDays(days); } - private List getDays(Elements tableHeaderCells) throws ParseException { + private List getDays(Elements tableHeaderCells) { List days = new ArrayList<>(); + int numberOfDays = tableHeaderCells.size(); - for (int i = 2; i < 7; i++) { + if (numberOfDays > 7) { + logger.info("Number of days: {}", numberOfDays); + } + + for (int i = 2; i < numberOfDays; i++) { String[] dayHeaderCell = tableHeaderCells.get(i).html().split("
"); - SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT); - Date d = sdf.parse(dayHeaderCell[1].trim()); - sdf.applyPattern("yyyy-MM-dd"); - TimetableDay day = new TimetableDay(); day.setDayName(dayHeaderCell[0]); - day.setDate(sdf.format(d)); + day.setDate(getFormattedDate(dayHeaderCell[1].trim())); if (tableHeaderCells.get(i).hasClass("free-day")) { day.setFreeDay(true); @@ -132,6 +136,11 @@ public class Timetable { private void addLessonInfoFromElement(Lesson lesson, Element e) { Elements spans = e.select("span"); + if (spans.isEmpty()) { + logger.warn("Lesson span is empty"); + return; + } + addTypeInfo(lesson, spans); addNormalLessonInfo(lesson, spans); addChangesInfo(lesson, spans); @@ -218,6 +227,10 @@ public class Timetable { } private String[] getLessonAndGroupInfoFromSpan(Element span) { + if (!span.text().contains("[")) { + return new String[] {span.text(), ""}; + } + String[] subjectNameArray = span.text().split(" "); String groupName = subjectNameArray[subjectNameArray.length - 1]; diff --git a/api/src/test/java/io/github/wulkanowy/api/DateTimeUtilsTest.kt b/api/src/test/java/io/github/wulkanowy/api/DateTimeUtilsTest.kt new file mode 100644 index 00000000..1105b2a7 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/DateTimeUtilsTest.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.api + +import org.junit.Assert +import org.junit.Test +import java.text.ParseException +import java.text.SimpleDateFormat +import java.util.* + +class DateTimeUtilsTest { + + @Test + fun getTicksDateObjectTest() { + val format = SimpleDateFormat("dd.MM.yyyy", Locale.ROOT) + format.timeZone = TimeZone.getTimeZone("UTC") + val date = format.parse("31.07.2017") + + Assert.assertEquals(636370560000000000L, getDateAsTick(date)) + + val calendar = Calendar.getInstance() + calendar.time = date + calendar.add(Calendar.DAY_OF_YEAR, -14) + val dateTwoWeekBefore = calendar.time + + Assert.assertEquals(636358464000000000L, getDateAsTick(dateTwoWeekBefore)) + } + + @Test(expected = ParseException::class) + fun getTicsStringInvalidFormatTest() { + Assert.assertEquals(636370560000000000L, getDateAsTick("31.07.2017", "dd.MMM.yyyy")) + } + + @Test + fun getTicsStringFormatTest() { + Assert.assertEquals(636370560000000000L, getDateAsTick("31.07.2017", "dd.MM.yyyy")) + } + + @Test + fun getTicsStringTest() { + Assert.assertEquals("636370560000000000", getDateAsTick("2017-07-31")) + Assert.assertEquals("636334272000000000", getDateAsTick("2017-06-19")) + Assert.assertEquals("636189120000000000", getDateAsTick("2017-01-02")) + Assert.assertEquals("636080256000000000", getDateAsTick("2016-08-29")) + } + + @Test + fun getDateTest() { + val format = SimpleDateFormat("dd.MM.yyyy", Locale.ROOT) + format.timeZone = TimeZone.getTimeZone("UTC") + val date = format.parse("31.07.2017") + + Assert.assertEquals(date, getDate(636370560000000000L)) + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java index 387d18f5..ba0c8f7f 100644 --- a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java @@ -11,6 +11,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import io.github.wulkanowy.api.generic.Semester; + public class StudentAndParentTest { private Client client; @@ -81,7 +83,7 @@ public class StudentAndParentTest { snp.getExtractedIdFromUrl("https://uonetplus-opiekun.vulcan.net.pl/demoupowiat/demo12345/Start/Index/")); } - @Test(expected = NotLoggedInErrorException.class) + @Test(expected = VulcanException.class) public void getExtractedIDNotLoggedTest() throws Exception { Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); StudentAndParent snp = new StudentAndParent(client, "symbol", null, null); @@ -129,8 +131,8 @@ public class StudentAndParentTest { @Test public void getDiariesAndStudentTest() throws IOException, VulcanException { - Document snpHome = Jsoup.parse(FixtureHelper.getAsString( - getClass().getResourceAsStream("StudentAndParent.html"))); + String input = FixtureHelper.getAsString(getClass().getResourceAsStream("WitrynaUczniaIRodzica.html")); + Document snpHome = Jsoup.parse(input); client = Mockito.mock(Client.class); Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(snpHome); diff --git a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java index 8d32291f..764e8009 100644 --- a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java +++ b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java @@ -5,6 +5,8 @@ import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.mockito.Mockito; +import io.github.wulkanowy.api.generic.Semester; + public abstract class StudentAndParentTestCase { protected StudentAndParent getSnp(String fixtureFileName) throws Exception { diff --git a/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java b/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java index 6eff84c9..deab7617 100644 --- a/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java @@ -19,12 +19,12 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getAllTest() throws Exception { - Assert.assertEquals(7, filled.getAll().size()); // 2 items are skipped + Assert.assertEquals(7, filled.getAll("").size()); // 2 items are skipped } @Test public void getSubjectTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("Zajęcia z wychowawcą", list.get(0).getSubject()); Assert.assertEquals("Język angielski", list.get(3).getSubject()); @@ -34,7 +34,7 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getValueTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("5", list.get(0).getValue()); Assert.assertEquals("5", list.get(3).getValue()); @@ -44,7 +44,7 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getColorTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("000000", list.get(0).getColor()); Assert.assertEquals("1289F7", list.get(3).getColor()); @@ -54,7 +54,7 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getSymbolTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("A1", list.get(0).getSymbol()); Assert.assertEquals("BW3", list.get(3).getSymbol()); @@ -65,7 +65,7 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getDescriptionTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("Dzień Kobiet w naszej klasie", list.get(0).getDescription()); Assert.assertEquals("Writing", list.get(3).getDescription()); @@ -76,7 +76,7 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getWeightTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("1,00", list.get(0).getWeight()); Assert.assertEquals("3,00", list.get(3).getWeight()); @@ -86,7 +86,7 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getDateTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("2017-03-21", list.get(0).getDate()); Assert.assertEquals("2017-06-02", list.get(3).getDate()); @@ -96,7 +96,7 @@ public class GradesListTest extends StudentAndParentTestCase { @Test public void getTeacherTest() throws Exception { - List list = filled.getAll(); + List list = filled.getAll(""); Assert.assertEquals("Patryk Maciejewski", list.get(0).getTeacher()); Assert.assertEquals("Oliwia Woźniak", list.get(3).getTeacher()); diff --git a/api/src/test/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt b/api/src/test/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt new file mode 100644 index 00000000..f67310c8 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.api.mobile + +import io.github.wulkanowy.api.StudentAndParentTestCase +import org.junit.Assert.assertEquals +import org.junit.Test + +class RegisterDeviceTest : StudentAndParentTestCase() { + + @Test + fun getTokenTest() { + val registration = RegisterDevice(getSnp("Rejestruj.html")) + + assertEquals("3S1A1B2C", registration.getToken().token) + assertEquals("Default", registration.getToken().symbol) + assertEquals("1234567", registration.getToken().pin) + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/mobile/RegisteredDevicesListTest.kt b/api/src/test/java/io/github/wulkanowy/api/mobile/RegisteredDevicesListTest.kt new file mode 100644 index 00000000..3cd8b97c --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/mobile/RegisteredDevicesListTest.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.api.mobile + +import io.github.wulkanowy.api.StudentAndParentTestCase +import org.junit.Assert.assertEquals +import org.junit.Test + +class RegisteredDevicesListTest : StudentAndParentTestCase() { + + private val filled = RegisteredDevices(getSnp("DostepMobilny-filled.html")) + + @Test + fun getListTest() { + assertEquals(2, filled.getList().size) + } + + @Test + fun getNameTest() { + assertEquals("google Android SDK built for x86", filled.getList()[0].name) + assertEquals("google (Android SDK) built for x86", filled.getList()[1].name) + } + + @Test + fun getSystemTest() { + assertEquals("Android 8.1.0", filled.getList()[0].system) + assertEquals("Android 8.1.0", filled.getList()[1].system) + } + + @Test + fun getDateTest() { + assertEquals("2018-01-20 22:35:30", filled.getList()[0].date) + } + + @Test + fun getIdTest() { + assertEquals(321, filled.getList()[0].id) + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java b/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java index d76c0648..304eb01a 100644 --- a/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java @@ -30,8 +30,8 @@ public class NotesListTest extends StudentAndParentTestCase { public void getDateTest() throws Exception { List filledList = filled.getAllNotes(); - Assert.assertEquals("06.06.2017", filledList.get(0).getDate()); - Assert.assertEquals("01.10.2016", filledList.get(2).getDate()); + Assert.assertEquals("2017-06-06", filledList.get(0).getDate()); + Assert.assertEquals("2016-10-01", filledList.get(2).getDate()); } @Test diff --git a/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java b/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java index 1383b830..7b3cce6e 100644 --- a/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java @@ -156,6 +156,7 @@ public class TimetableTest extends StudentAndParentTestCase { Assert.assertEquals("poprzednio: Wychowanie fizyczne", full.getWeekTable().getDay(4).getLesson(2).getDescription()); Assert.assertEquals("egzamin", full.getWeekTable().getDay(3).getLesson(0).getDescription()); Assert.assertEquals("", full.getWeekTable().getDay(4).getLesson(1).getDescription()); + Assert.assertEquals("poprzednio: Zajęcia z wychowawcą", full.getWeekTable().getDay(4).getLesson(5).getDescription()); Assert.assertEquals("opis w uwadze bez klasy w spanie", full.getWeekTable().getDay(4).getLesson(4).getDescription()); Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getDescription()); } diff --git a/api/src/test/resources/io/github/wulkanowy/api/StudentAndParent.html b/api/src/test/resources/io/github/wulkanowy/api/WitrynaUczniaIRodzica.html similarity index 100% rename from api/src/test/resources/io/github/wulkanowy/api/StudentAndParent.html rename to api/src/test/resources/io/github/wulkanowy/api/WitrynaUczniaIRodzica.html diff --git a/api/src/test/resources/io/github/wulkanowy/api/mobile/DostepMobilny-filled.html b/api/src/test/resources/io/github/wulkanowy/api/mobile/DostepMobilny-filled.html new file mode 100644 index 00000000..9fbfd403 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/mobile/DostepMobilny-filled.html @@ -0,0 +1,44 @@ + + + + + Witryna ucznia i rodzica – dostęp mobilny + + + +
+

Dostęp mobilny

+ +
+

Zarejestrowane urządzenia

+
+ + + + + + + + + + + + + + + + + + + + +
UrządzenieData rejestracji
google Android SDK built for x86 (Android 8.1.0)20.01.2018 godz: 22:35:30 + Wyrejestruj +
google (Android SDK) built for x86 (Android 8.1.0)20.01.2018 godz: 22:35:30 + Wyrejestruj +
+
+ +
wersja: 18.01.0001.27311
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/mobile/Rejestruj.html b/api/src/test/resources/io/github/wulkanowy/api/mobile/Rejestruj.html new file mode 100644 index 00000000..15d08d07 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/mobile/Rejestruj.html @@ -0,0 +1,26 @@ + + + + + Witryna ucznia i rodzica – Rejestracja urządzenia mobilnego + + + +
+

Rejestracja urządzenia mobilnego

+
+ Za pomocą aplikacji "Dzienniczek+" zeskanuj kod QR. + Kod QR + Token: 3S1A1B2C + Symbol: Default + PIN: 1234567 +
+
+
wersja: 18.01.0001.27311
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-full.html b/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-full.html index c6ade3a5..c3dbfebe 100644 --- a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-full.html +++ b/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-full.html @@ -364,7 +364,20 @@ - + +
+ Tworzenie i administrowanie bazami danych [zaw2] + + + +
+
+ Zajęcia z wychowawcą + Małgorzata Kowal + 43 + (zmiana organizacji zajęć) +
+ 6 diff --git a/app/build.gradle b/app/build.gradle index 35559640..dc655e90 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -41,8 +41,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 targetSdkVersion 26 - versionCode 12 - versionName "0.4.4" + versionCode 14 + versionName "0.5.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true playAccountConfig = playAccountConfigs.defaultAccountConfig @@ -118,6 +118,9 @@ dependencies { implementation "com.jakewharton.threetenabp:threetenabp:$threeTenABP" implementation "com.google.android.gms:play-services-oss-licenses:$ossLicenses" + implementation "com.jakewharton.timber:timber:$timber" + implementation "at.favre.lib:slf4j-timber:$slf4jTimber" + implementation("com.crashlytics.sdk.android:crashlytics:$crashlyticsSdk@aar") { transitive = true } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 24b5e188..dd982626 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,7 +14,6 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" - android:supportsRtl="true" android:theme="@style/WulkanowyTheme"> + /> + android:theme="@style/WulkanowyTheme.DarkActionBar" /> + android:theme="@style/WulkanowyTheme.DarkActionBar" /> applicationInjector() { + return DaggerAppComponent.builder().create(this); } } diff --git a/app/src/main/java/io/github/wulkanowy/data/Repository.java b/app/src/main/java/io/github/wulkanowy/data/Repository.java index 1516c027..ac6a8b42 100644 --- a/app/src/main/java/io/github/wulkanowy/data/Repository.java +++ b/app/src/main/java/io/github/wulkanowy/data/Repository.java @@ -47,4 +47,10 @@ public class Repository implements RepositoryContract { public SyncContract getSyncRepo() { return synchronization; } + + @Override + public void cleanAllData() { + sharedPref.cleanSharedPref(); + database.recreateDatabase(); + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java b/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java index ffcfb046..e4dbd26b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java @@ -17,4 +17,6 @@ public interface RepositoryContract { DbContract getDbRepo(); SyncContract getSyncRepo(); + + void cleanAllData(); } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbContract.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbContract.java index 34541d3e..f32884f5 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbContract.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbContract.java @@ -30,4 +30,6 @@ public interface DbContract { long getCurrentSemesterId(); int getCurrentSemesterName(); + + void recreateDatabase(); } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java index 97bb7559..904fa343 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java @@ -12,6 +12,7 @@ import java.util.Comparator; import java.util.List; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import io.github.wulkanowy.api.Vulcan; @@ -21,9 +22,7 @@ import io.github.wulkanowy.data.db.dao.migrations.Migration26; import io.github.wulkanowy.data.db.dao.migrations.Migration27; import io.github.wulkanowy.data.db.dao.migrations.Migration28; import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.di.annotations.ApplicationContext; -import io.github.wulkanowy.di.annotations.DatabaseInfo; -import io.github.wulkanowy.utils.LogUtils; +import timber.log.Timber; @Singleton public class DbHelper extends DaoMaster.OpenHelper { @@ -33,7 +32,7 @@ public class DbHelper extends DaoMaster.OpenHelper { private final Vulcan vulcan; @Inject - DbHelper(@ApplicationContext Context context, @DatabaseInfo String dbName, + DbHelper(Context context, @Named("dbName") String dbName, SharedPrefContract sharedPref, Vulcan vulcan) { super(context, dbName); this.sharedPref = sharedPref; @@ -42,7 +41,7 @@ public class DbHelper extends DaoMaster.OpenHelper { @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { - LogUtils.info("Cleaning user data oldVersion=" + oldVersion + " newVersion=" + newVersion); + Timber.i("Cleaning user data oldVersion=%s newVersion=%s", oldVersion, newVersion); Database database = new StandardDatabase(db); recreateDatabase(database); } @@ -55,11 +54,11 @@ public class DbHelper extends DaoMaster.OpenHelper { for (Migration migration : migrations) { if (oldVersion < migration.getVersion()) { try { - LogUtils.info("Applying migration to db schema v" + migration.getVersion() + "..."); + Timber.i("Applying migration to db schema v%s...", migration.getVersion()); migration.runMigration(db, sharedPref, vulcan); - LogUtils.info("Migration " + migration.getVersion() + " complete"); + Timber.i("Migration %s complete", migration.getVersion()); } catch (Exception e) { - e.printStackTrace(); + Timber.e(e, "Failed to apply migration"); recreateDatabase(db); break; } @@ -68,7 +67,7 @@ public class DbHelper extends DaoMaster.OpenHelper { } private void recreateDatabase(Database db) { - LogUtils.info("Database is recreating..."); + Timber.i("Database is recreating..."); sharedPref.setCurrentUserId(0); DaoMaster.dropAllTables(db, true); onCreate(db); diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbRepository.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbRepository.java index 43eee87e..2c92c55d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbRepository.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbRepository.java @@ -1,9 +1,12 @@ package io.github.wulkanowy.data.db.dao; +import org.greenrobot.greendao.database.Database; + import java.util.List; import javax.inject.Inject; +import io.github.wulkanowy.data.db.dao.entities.DaoMaster; import io.github.wulkanowy.data.db.dao.entities.DaoSession; import io.github.wulkanowy.data.db.dao.entities.DiaryDao; import io.github.wulkanowy.data.db.dao.entities.Grade; @@ -109,4 +112,12 @@ public class DbRepository implements DbContract { SemesterDao.Properties.Current.eq(true) ).unique(); } + + @Override + public void recreateDatabase() { + Database database = daoSession.getDatabase(); + + DaoMaster.dropAllTables(database, true); + DaoMaster.createAllTables(database, true); + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration23.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration23.java index b32c6261..b2a41ad1 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration23.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration23.java @@ -9,8 +9,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import io.github.wulkanowy.api.Diary; import io.github.wulkanowy.api.Vulcan; +import io.github.wulkanowy.api.generic.Diary; import io.github.wulkanowy.data.db.dao.DbHelper; import io.github.wulkanowy.data.db.shared.SharedPrefContract; import io.github.wulkanowy.utils.security.Scrambler; diff --git a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesRepository.java b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesRepository.java index a430e335..f8a1baaa 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesRepository.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesRepository.java @@ -3,8 +3,6 @@ package io.github.wulkanowy.data.db.resources; import android.content.Context; import android.content.res.Resources; -import com.crashlytics.android.Crashlytics; - import java.io.IOException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; @@ -15,10 +13,9 @@ import javax.inject.Singleton; import io.github.wulkanowy.R; import io.github.wulkanowy.api.NotLoggedInErrorException; 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; import io.github.wulkanowy.utils.security.CryptoException; +import timber.log.Timber; @Singleton public class ResourcesRepository implements ResourcesContract { @@ -26,7 +23,7 @@ public class ResourcesRepository implements ResourcesContract { private Resources resources; @Inject - ResourcesRepository(@ApplicationContext Context context) { + ResourcesRepository(Context context) { resources = context.getResources(); } @@ -42,8 +39,7 @@ public class ResourcesRepository implements ResourcesContract { @Override public String getErrorLoginMessage(Exception exception) { - LogUtils.error(AppConstant.APP_NAME + " encountered a error", exception); - Crashlytics.logException(exception); + Timber.e(exception,"%s encountered a error", AppConstant.APP_NAME); if (exception instanceof CryptoException) { return resources.getString(R.string.encrypt_failed_text); diff --git a/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefContract.java b/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefContract.java index b9d3fd14..370f8dfe 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefContract.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefContract.java @@ -21,6 +21,8 @@ public interface SharedPrefContract { boolean isShowAttendancePresent(); + int getCurrentTheme(); + int getServicesInterval(); boolean isMobileDisable(); @@ -28,4 +30,6 @@ public interface SharedPrefContract { boolean isServicesEnable(); boolean isNotifyEnable(); + + void cleanSharedPref(); } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefRepository.java b/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefRepository.java index bf46ce35..adedbda4 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefRepository.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefRepository.java @@ -6,10 +6,9 @@ import android.content.SharedPreferences; import android.preference.PreferenceManager; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; -import io.github.wulkanowy.di.annotations.ApplicationContext; -import io.github.wulkanowy.di.annotations.SharedPreferencesInfo; import io.github.wulkanowy.ui.main.settings.SettingsFragment; @Singleton @@ -24,7 +23,7 @@ public class SharedPrefRepository implements SharedPrefContract { private final SharedPreferences settingsSharedPref; @Inject - SharedPrefRepository(@ApplicationContext Context context, @SharedPreferencesInfo String sharedName) { + SharedPrefRepository(Context context, @Named("sharedPrefName") String sharedName) { appSharedPref = context.getSharedPreferences(sharedName, Context.MODE_PRIVATE); settingsSharedPref = PreferenceManager.getDefaultSharedPreferences(context); } @@ -70,6 +69,11 @@ public class SharedPrefRepository implements SharedPrefContract { return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_ATTENDANCE_PRESENT, false); } + @Override + public int getCurrentTheme() { + return Integer.parseInt(settingsSharedPref.getString(SettingsFragment.SHARED_KEY_THEME, "1")); + } + @Override public int getServicesInterval() { return Integer.parseInt(settingsSharedPref.getString(SettingsFragment.SHARED_KEY_SERVICES_INTERVAL, "60")); @@ -89,4 +93,10 @@ public class SharedPrefRepository implements SharedPrefContract { public boolean isMobileDisable() { return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_SERVICES_MOBILE_DISABLED, false); } + + @Override + public void cleanSharedPref() { + appSharedPref.edit().clear().apply(); + settingsSharedPref.edit().clear().apply(); + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/AccountSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/AccountSync.java index 27b497c7..b9f72cb2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/sync/AccountSync.java +++ b/app/src/main/java/io/github/wulkanowy/data/sync/AccountSync.java @@ -23,11 +23,10 @@ import io.github.wulkanowy.data.db.dao.entities.StudentDao; import io.github.wulkanowy.data.db.dao.entities.Symbol; import io.github.wulkanowy.data.db.dao.entities.SymbolDao; import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.di.annotations.ApplicationContext; import io.github.wulkanowy.utils.DataObjectConverter; -import io.github.wulkanowy.utils.LogUtils; import io.github.wulkanowy.utils.security.CryptoException; import io.github.wulkanowy.utils.security.Scrambler; +import timber.log.Timber; @Singleton public class AccountSync { @@ -42,7 +41,7 @@ public class AccountSync { @Inject AccountSync(DaoSession daoSession, SharedPrefContract sharedPref, - Vulcan vulcan, @ApplicationContext Context context) { + Vulcan vulcan, Context context) { this.daoSession = daoSession; this.sharedPref = sharedPref; this.vulcan = vulcan; @@ -74,7 +73,7 @@ public class AccountSync { } private Account insertAccount(String email, String password) throws CryptoException { - LogUtils.debug("Register account: " + email); + Timber.d("Register account"); Account account = new Account() .setEmail(email) .setPassword(Scrambler.encrypt(email, password, context)); @@ -83,10 +82,11 @@ public class AccountSync { } private Symbol insertSymbol(Account account) throws VulcanException, IOException { - LogUtils.debug("Register symbol: " + vulcan.getSymbol()); + String schoolId = vulcan.getStudentAndParent().getSchoolID(); + Timber.d("Register symbol %s", vulcan.getSymbol()); Symbol symbol = new Symbol() .setUserId(account.getId()) - .setSchoolId(vulcan.getStudentAndParent().getSchoolID()) + .setSchoolId(schoolId) .setSymbol(vulcan.getSymbol()); daoSession.getSymbolDao().insert(symbol); @@ -98,7 +98,7 @@ public class AccountSync { vulcan.getStudentAndParent().getStudents(), symbol.getId() ); - LogUtils.debug("Register students: " + studentList.size()); + Timber.d("Register students %s", studentList.size()); daoSession.getStudentDao().insertInTx(studentList); } @@ -109,7 +109,7 @@ public class AccountSync { StudentDao.Properties.SymbolId.eq(symbolEntity.getId()), StudentDao.Properties.Current.eq(true) ).unique().getId()); - LogUtils.debug("Register diaries: " + diaryList.size()); + Timber.d("Register diaries %s", diaryList.size()); daoSession.getDiaryDao().insertInTx(diaryList); } @@ -119,7 +119,7 @@ public class AccountSync { daoSession.getDiaryDao().queryBuilder().where( DiaryDao.Properties.Current.eq(true) ).unique().getId()); - LogUtils.debug("Register semesters: " + semesterList.size()); + Timber.d("Register semesters %s", semesterList.size()); daoSession.getSemesterDao().insertInTx(semesterList); } @@ -131,7 +131,7 @@ public class AccountSync { throw new NotRegisteredUserException("Can't find user id in SharedPreferences"); } - LogUtils.debug("Initialization current user id=" + userId); + Timber.d("Init current user (%s)", userId); Account account = daoSession.getAccountDao().load(userId); diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/AttendanceSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/AttendanceSync.java index 59a6e1a3..18769418 100644 --- a/app/src/main/java/io/github/wulkanowy/data/sync/AttendanceSync.java +++ b/app/src/main/java/io/github/wulkanowy/data/sync/AttendanceSync.java @@ -1,7 +1,6 @@ package io.github.wulkanowy.data.sync; import java.io.IOException; -import java.text.ParseException; import java.util.ArrayList; import java.util.List; @@ -19,8 +18,7 @@ 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.utils.DataObjectConverter; -import io.github.wulkanowy.utils.LogUtils; -import io.github.wulkanowy.utils.TimeUtils; +import timber.log.Timber; @Singleton public class AttendanceSync { @@ -37,10 +35,10 @@ public class AttendanceSync { this.vulcan = vulcan; } - public void syncAttendance(long diaryId, String date) throws IOException, ParseException, VulcanException { + public void syncAttendance(long diaryId, String date) throws IOException, VulcanException { this.diaryId = diaryId; - io.github.wulkanowy.api.generic.Week weekApi = getWeekFromApi(getNormalizedDate(date)); + io.github.wulkanowy.api.generic.Week weekApi = getWeekFromApi(date); Week weekDb = getWeekFromDb(weekApi.getStartDayDate()); long weekId = updateWeekInDb(weekDb, weekApi); @@ -49,15 +47,11 @@ public class AttendanceSync { daoSession.getAttendanceLessonDao().saveInTx(lessonList); - LogUtils.debug("Synchronization attendance lessons (amount = " + lessonList.size() + ")"); - } - - private String getNormalizedDate(String date) throws ParseException { - return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : ""; + Timber.d("Attendance synchronization complete (%s)", lessonList.size()); } private io.github.wulkanowy.api.generic.Week getWeekFromApi(String date) - throws IOException, ParseException, VulcanException { + throws IOException, VulcanException { return vulcan.getAttendanceTable().getWeekTable(date); } diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/ExamsSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/ExamsSync.java index 53470882..db473092 100644 --- a/app/src/main/java/io/github/wulkanowy/data/sync/ExamsSync.java +++ b/app/src/main/java/io/github/wulkanowy/data/sync/ExamsSync.java @@ -1,7 +1,6 @@ package io.github.wulkanowy.data.sync; import java.io.IOException; -import java.text.ParseException; import java.util.ArrayList; import java.util.List; @@ -18,8 +17,7 @@ import io.github.wulkanowy.data.db.dao.entities.ExamDao; import io.github.wulkanowy.data.db.dao.entities.Week; import io.github.wulkanowy.data.db.dao.entities.WeekDao; import io.github.wulkanowy.utils.DataObjectConverter; -import io.github.wulkanowy.utils.LogUtils; -import io.github.wulkanowy.utils.TimeUtils; +import timber.log.Timber; public class ExamsSync { @@ -35,11 +33,10 @@ public class ExamsSync { this.vulcan = vulcan; } - public void syncExams(long diaryId, String date) throws IOException, VulcanException, - ParseException { + public void syncExams(long diaryId, String date) throws IOException, VulcanException { this.diaryId = diaryId; - io.github.wulkanowy.api.generic.Week weekApi = getWeekFromApi(getNormalizedDate(date)); + io.github.wulkanowy.api.generic.Week weekApi = getWeekFromApi(date); Week weekDb = getWeekFromDb(weekApi.getStartDayDate()); long weekId = updateWeekInDb(weekDb, weekApi); @@ -48,7 +45,7 @@ public class ExamsSync { daoSession.getExamDao().saveInTx(examList); - LogUtils.debug("Synchronization exams (amount = " + examList.size() + ")"); + Timber.d("Exams synchronization complete (%s)", examList.size()); } private Week getWeekFromDb(String date) { @@ -59,7 +56,7 @@ public class ExamsSync { } private io.github.wulkanowy.api.generic.Week getWeekFromApi(String date) - throws VulcanException, IOException, ParseException { + throws VulcanException, IOException { return vulcan.getExamsList().getWeek(date, true); } @@ -77,10 +74,6 @@ public class ExamsSync { return daoSession.getWeekDao().insert(weekApiEntity); } - private String getNormalizedDate(String date) throws ParseException { - return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : ""; - } - private Day getDayFromDb(String date, long weekId) { return daoSession.getDayDao().queryBuilder().where( DayDao.Properties.WeekId.eq(weekId), diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/GradeSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/GradeSync.java index 0b70695d..59a71845 100644 --- a/app/src/main/java/io/github/wulkanowy/data/sync/GradeSync.java +++ b/app/src/main/java/io/github/wulkanowy/data/sync/GradeSync.java @@ -1,7 +1,6 @@ package io.github.wulkanowy.data.sync; import java.io.IOException; -import java.text.ParseException; import java.util.ArrayList; import java.util.List; @@ -16,7 +15,7 @@ import io.github.wulkanowy.data.db.dao.entities.Semester; import io.github.wulkanowy.data.db.dao.entities.SubjectDao; import io.github.wulkanowy.utils.DataObjectConverter; import io.github.wulkanowy.utils.EntitiesCompare; -import io.github.wulkanowy.utils.LogUtils; +import timber.log.Timber; @Singleton public class GradeSync { @@ -33,7 +32,7 @@ public class GradeSync { this.vulcan = vulcan; } - public void sync(long semesterId) throws IOException, VulcanException, ParseException { + public void sync(long semesterId) throws IOException, VulcanException { this.semesterId = semesterId; Semester semester = daoSession.getSemesterDao().load(semesterId); @@ -44,7 +43,7 @@ public class GradeSync { daoSession.getGradeDao().deleteInTx(semester.getGradeList()); daoSession.getGradeDao().insertInTx(lastList); - LogUtils.debug("Synchronization grades (amount = " + lastList.size() + ")"); + Timber.d("Grades synchronization complete (%s)", lastList.size()); } private void resetSemesterRelations(Semester semester) { @@ -64,7 +63,7 @@ public class GradeSync { return updatedList; } - private List getComparedList(Semester semester) throws IOException, VulcanException, ParseException { + private List getComparedList(Semester semester) throws IOException, VulcanException { List gradesFromNet = DataObjectConverter.gradesToGradeEntities( vulcan.getGradesList().getAll(semester.getValue()), semesterId); diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/SubjectSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/SubjectSync.java index 163a6234..1d0300d6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/sync/SubjectSync.java +++ b/app/src/main/java/io/github/wulkanowy/data/sync/SubjectSync.java @@ -13,7 +13,7 @@ import io.github.wulkanowy.data.db.dao.entities.DaoSession; import io.github.wulkanowy.data.db.dao.entities.Semester; import io.github.wulkanowy.data.db.dao.entities.Subject; import io.github.wulkanowy.utils.DataObjectConverter; -import io.github.wulkanowy.utils.LogUtils; +import timber.log.Timber; @Singleton public class SubjectSync { @@ -40,7 +40,7 @@ public class SubjectSync { daoSession.getSubjectDao().deleteInTx(getSubjectsFromDb()); daoSession.getSubjectDao().insertInTx(lastList); - LogUtils.debug("Synchronization subjects (amount = " + lastList.size() + ")"); + Timber.d("Subjects synchronization complete (%s)", lastList.size()); } private List getSubjectsFromNet(Semester semester) throws VulcanException, IOException { diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/SyncRepository.java b/app/src/main/java/io/github/wulkanowy/data/sync/SyncRepository.java index 6a1e4dc1..05dd14fd 100644 --- a/app/src/main/java/io/github/wulkanowy/data/sync/SyncRepository.java +++ b/app/src/main/java/io/github/wulkanowy/data/sync/SyncRepository.java @@ -1,7 +1,6 @@ package io.github.wulkanowy.data.sync; import java.io.IOException; -import java.text.ParseException; import javax.inject.Inject; import javax.inject.Singleton; @@ -47,17 +46,17 @@ public class SyncRepository implements SyncContract { } @Override - public void initLastUser() throws IOException, CryptoException { + public void initLastUser() throws CryptoException { accountSync.initLastUser(); } @Override - public void syncGrades(int semesterName) throws VulcanException, IOException, ParseException { + public void syncGrades(int semesterName) throws VulcanException, IOException { gradeSync.sync(semesterName); } @Override - public void syncGrades() throws VulcanException, IOException, ParseException { + public void syncGrades() throws VulcanException, IOException { gradeSync.sync(database.getCurrentSemesterId()); } @@ -72,12 +71,12 @@ public class SyncRepository implements SyncContract { } @Override - public void syncAttendance() throws ParseException, IOException, VulcanException { + public void syncAttendance() throws IOException, VulcanException { attendanceSync.syncAttendance(database.getCurrentDiaryId(), null); } @Override - public void syncAttendance(long diaryId, String date) throws ParseException, IOException, VulcanException { + public void syncAttendance(long diaryId, String date) throws IOException, VulcanException { if (diaryId != 0) { attendanceSync.syncAttendance(diaryId, date); } else { @@ -86,12 +85,12 @@ public class SyncRepository implements SyncContract { } @Override - public void syncTimetable() throws VulcanException, IOException, ParseException { + public void syncTimetable() throws VulcanException, IOException { timetableSync.syncTimetable(database.getCurrentDiaryId(), null); } @Override - public void syncTimetable(long diaryId, String date) throws VulcanException, IOException, ParseException { + public void syncTimetable(long diaryId, String date) throws VulcanException, IOException { if (diaryId != 0) { timetableSync.syncTimetable(diaryId, date); } else { @@ -100,12 +99,12 @@ public class SyncRepository implements SyncContract { } @Override - public void syncExams() throws VulcanException, IOException, ParseException { + public void syncExams() throws VulcanException, IOException { examsSync.syncExams(database.getCurrentDiaryId(), null); } @Override - public void syncExams(long diaryId, String date) throws VulcanException, IOException, ParseException { + public void syncExams(long diaryId, String date) throws VulcanException, IOException { if (diaryId != 0) { examsSync.syncExams(diaryId, date); } else { @@ -114,7 +113,7 @@ public class SyncRepository implements SyncContract { } @Override - public void syncAll() throws VulcanException, IOException, ParseException { + public void syncAll() throws VulcanException, IOException { syncSubjects(); syncGrades(); syncAttendance(); diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/TimetableSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/TimetableSync.java index 3fe22aff..1ea2fa48 100644 --- a/app/src/main/java/io/github/wulkanowy/data/sync/TimetableSync.java +++ b/app/src/main/java/io/github/wulkanowy/data/sync/TimetableSync.java @@ -3,7 +3,6 @@ package io.github.wulkanowy.data.sync; import org.apache.commons.collections4.CollectionUtils; import java.io.IOException; -import java.text.ParseException; import java.util.ArrayList; import java.util.List; @@ -21,8 +20,7 @@ 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.utils.DataObjectConverter; -import io.github.wulkanowy.utils.LogUtils; -import io.github.wulkanowy.utils.TimeUtils; +import timber.log.Timber; @Singleton public class TimetableSync { @@ -39,10 +37,10 @@ public class TimetableSync { this.vulcan = vulcan; } - public void syncTimetable(long diaryId, String date) throws IOException, ParseException, VulcanException { + public void syncTimetable(long diaryId, String date) throws IOException, VulcanException { this.diaryId = diaryId; - io.github.wulkanowy.api.generic.Week weekApi = getWeekFromApi(getNormalizedDate(date)); + io.github.wulkanowy.api.generic.Week weekApi = getWeekFromApi(date); Week weekDb = getWeekFromDb(weekApi.getStartDayDate()); long weekId = updateWeekInDb(weekDb, weekApi); @@ -51,15 +49,11 @@ public class TimetableSync { daoSession.getTimetableLessonDao().saveInTx(lessonList); - LogUtils.debug("Synchronization timetable lessons (amount = " + lessonList.size() + ")"); - } - - private String getNormalizedDate(String date) throws ParseException { - return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : ""; + Timber.d("Timetable synchronization complete (%s)", lessonList.size()); } private io.github.wulkanowy.api.generic.Week getWeekFromApi(String date) - throws IOException, ParseException, VulcanException { + throws IOException, VulcanException { return vulcan.getTimetable().getWeekTable(date); } diff --git a/app/src/main/java/io/github/wulkanowy/di/AppComponent.java b/app/src/main/java/io/github/wulkanowy/di/AppComponent.java new file mode 100644 index 00000000..92f5d9a1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/AppComponent.java @@ -0,0 +1,20 @@ +package io.github.wulkanowy.di; + +import javax.inject.Singleton; + +import dagger.Component; +import dagger.android.AndroidInjector; +import dagger.android.support.AndroidSupportInjectionModule; +import io.github.wulkanowy.WulkanowyApp; + +@Singleton +@Component(modules = { + AndroidSupportInjectionModule.class, + AppModule.class, + BuilderModule.class +}) +public interface AppComponent extends AndroidInjector { + @Component.Builder + abstract class Builder extends AndroidInjector.Builder { + } +} diff --git a/app/src/main/java/io/github/wulkanowy/di/AppModule.java b/app/src/main/java/io/github/wulkanowy/di/AppModule.java new file mode 100644 index 00000000..d6d4dfa2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/AppModule.java @@ -0,0 +1,78 @@ +package io.github.wulkanowy.di; + +import android.content.Context; + +import javax.inject.Named; +import javax.inject.Singleton; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import io.github.wulkanowy.WulkanowyApp; +import io.github.wulkanowy.api.Vulcan; +import io.github.wulkanowy.data.Repository; +import io.github.wulkanowy.data.RepositoryContract; +import io.github.wulkanowy.data.db.dao.DbContract; +import io.github.wulkanowy.data.db.dao.DbHelper; +import io.github.wulkanowy.data.db.dao.DbRepository; +import io.github.wulkanowy.data.db.dao.entities.DaoMaster; +import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.resources.ResourcesContract; +import io.github.wulkanowy.data.db.resources.ResourcesRepository; +import io.github.wulkanowy.data.db.shared.SharedPrefContract; +import io.github.wulkanowy.data.db.shared.SharedPrefRepository; +import io.github.wulkanowy.data.sync.SyncContract; +import io.github.wulkanowy.data.sync.SyncRepository; +import io.github.wulkanowy.utils.AppConstant; + +@Module +public abstract class AppModule { + + @Binds + abstract Context provideContext(WulkanowyApp app); + + @Singleton + @Binds + abstract RepositoryContract provideRepository(Repository repository); + + @Singleton + @Binds + abstract DbContract provideDatabse(DbRepository dbRepository); + + @Singleton + @Binds + abstract SharedPrefContract provideSharedPref(SharedPrefRepository sharedPrefRepository); + + @Singleton + @Binds + abstract SyncContract provideSync(SyncRepository syncRepository); + + @Singleton + @Binds + abstract ResourcesContract provideResources(ResourcesRepository resourcesRepository); + + @Singleton + @Provides + static DaoSession provideDaoSession(DbHelper dbHelper) { + return new DaoMaster(dbHelper.getWritableDb()).newSession(); + } + + @Singleton + @Provides + static Vulcan provideVulcan() { + return new Vulcan(); + } + + @Provides + @Named("dbName") + static String provideDbName() { + return AppConstant.DATABASE_NAME; + } + + @Provides + @Named("sharedPrefName") + static String provideSharedPrefName() { + return AppConstant.SHARED_PREFERENCES_NAME; + } + +} diff --git a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.java b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.java new file mode 100644 index 00000000..74b77d52 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.java @@ -0,0 +1,39 @@ +package io.github.wulkanowy.di; + +import dagger.Module; +import dagger.android.ContributesAndroidInjector; +import io.github.wulkanowy.di.scopes.PerActivity; +import io.github.wulkanowy.services.jobs.SyncJob; +import io.github.wulkanowy.services.widgets.TimetableWidgetServices; +import io.github.wulkanowy.ui.login.LoginActivity; +import io.github.wulkanowy.ui.login.LoginModule; +import io.github.wulkanowy.ui.main.MainActivity; +import io.github.wulkanowy.ui.main.MainModule; +import io.github.wulkanowy.ui.splash.SplashActivity; +import io.github.wulkanowy.ui.splash.SplashModule; +import io.github.wulkanowy.ui.widgets.TimetableWidgetProvider; + +@Module +abstract class BuilderModule { + + @PerActivity + @ContributesAndroidInjector(modules = SplashModule.class) + abstract SplashActivity bindSplashActivity(); + + @PerActivity + @ContributesAndroidInjector(modules = LoginModule.class) + abstract LoginActivity bindLoginActivity(); + + @PerActivity + @ContributesAndroidInjector(modules = MainModule.class) + abstract MainActivity bindMainActivity(); + + @ContributesAndroidInjector + abstract SyncJob bindSyncJob(); + + @ContributesAndroidInjector + abstract TimetableWidgetServices bindTimetableWidgetServices(); + + @ContributesAndroidInjector + abstract TimetableWidgetProvider bindTimetableWidgetProvider(); +} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/ActivityContext.java b/app/src/main/java/io/github/wulkanowy/di/annotations/ActivityContext.java deleted file mode 100644 index 2a74c32d..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/ActivityContext.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Qualifier; - -@Qualifier -@Retention(RetentionPolicy.RUNTIME) -public @interface ActivityContext { -} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/ApplicationContext.java b/app/src/main/java/io/github/wulkanowy/di/annotations/ApplicationContext.java deleted file mode 100644 index 04139d99..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/ApplicationContext.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Qualifier; - -@Qualifier -@Retention(RetentionPolicy.RUNTIME) -public @interface ApplicationContext { -} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/SharedPreferencesInfo.java b/app/src/main/java/io/github/wulkanowy/di/annotations/SharedPreferencesInfo.java deleted file mode 100644 index 919c77a0..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/SharedPreferencesInfo.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Qualifier; - -@Qualifier -@Retention(RetentionPolicy.RUNTIME) -public @interface SharedPreferencesInfo { -} diff --git a/app/src/main/java/io/github/wulkanowy/di/component/ActivityComponent.java b/app/src/main/java/io/github/wulkanowy/di/component/ActivityComponent.java deleted file mode 100644 index 3365a317..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/component/ActivityComponent.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.wulkanowy.di.component; - -import dagger.Component; -import io.github.wulkanowy.di.annotations.PerActivity; -import io.github.wulkanowy.di.modules.ActivityModule; -import io.github.wulkanowy.ui.login.LoginActivity; -import io.github.wulkanowy.ui.main.MainActivity; -import io.github.wulkanowy.ui.splash.SplashActivity; - -@PerActivity -@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) -public interface ActivityComponent { - - void inject(SplashActivity splashActivity); - - void inject(LoginActivity loginActivity); - - void inject(MainActivity mainActivity); -} diff --git a/app/src/main/java/io/github/wulkanowy/di/component/ApplicationComponent.java b/app/src/main/java/io/github/wulkanowy/di/component/ApplicationComponent.java deleted file mode 100644 index d4bf7bb7..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/component/ApplicationComponent.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.di.component; - -import android.content.Context; - -import javax.inject.Singleton; - -import dagger.Component; -import io.github.wulkanowy.WulkanowyApp; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.di.annotations.ApplicationContext; -import io.github.wulkanowy.di.modules.ApplicationModule; -import io.github.wulkanowy.services.jobs.SyncJob; -import io.github.wulkanowy.ui.widgets.TimetableWidgetFactory; -import io.github.wulkanowy.ui.widgets.TimetableWidgetProvider; - -@Singleton -@Component(modules = ApplicationModule.class) -public interface ApplicationComponent { - - @ApplicationContext - Context getContext(); - - RepositoryContract getRepository(); - - void inject(WulkanowyApp wulkanowyApp); - - void inject(SyncJob syncJob); - - void inject(TimetableWidgetFactory timetableWidgetFactory); - - void inject(TimetableWidgetProvider timetableWidgetProvider); -} diff --git a/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java b/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java deleted file mode 100644 index 9f56364c..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.di.component; - -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.exams.ExamsFragment; -import io.github.wulkanowy.ui.main.exams.ExamsTabFragment; -import io.github.wulkanowy.ui.main.grades.GradesFragment; -import io.github.wulkanowy.ui.main.timetable.TimetableFragment; -import io.github.wulkanowy.ui.main.timetable.TimetableTabFragment; - -@PerFragment -@Component(dependencies = ApplicationComponent.class, modules = FragmentModule.class) -public interface FragmentComponent { - - void inject(GradesFragment gradesFragment); - - void inject(AttendanceFragment attendanceFragment); - - void inject(AttendanceTabFragment attendanceTabFragment); - - void inject(ExamsFragment examsFragment); - - void inject(ExamsTabFragment examsTabFragment); - - void inject(TimetableFragment timetableFragment); - - void inject(TimetableTabFragment timetableTabFragment); -} diff --git a/app/src/main/java/io/github/wulkanowy/di/modules/ActivityModule.java b/app/src/main/java/io/github/wulkanowy/di/modules/ActivityModule.java deleted file mode 100644 index cdffe73a..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/modules/ActivityModule.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.github.wulkanowy.di.modules; - - -import android.content.Context; -import android.support.v7.app.AppCompatActivity; - -import dagger.Module; -import dagger.Provides; -import io.github.wulkanowy.di.annotations.ActivityContext; -import io.github.wulkanowy.di.annotations.PerActivity; -import io.github.wulkanowy.ui.base.BasePagerAdapter; -import io.github.wulkanowy.ui.login.LoginContract; -import io.github.wulkanowy.ui.login.LoginPresenter; -import io.github.wulkanowy.ui.main.MainContract; -import io.github.wulkanowy.ui.main.MainPresenter; -import io.github.wulkanowy.ui.splash.SplashContract; -import io.github.wulkanowy.ui.splash.SplashPresenter; - -@Module -public class ActivityModule { - - private AppCompatActivity activity; - - public ActivityModule(AppCompatActivity activity) { - this.activity = activity; - } - - @ActivityContext - @Provides - Context provideContext() { - return activity; - } - - @Provides - AppCompatActivity provideActivity() { - return activity; - } - - @PerActivity - @Provides - SplashContract.Presenter provideSplashPresenter - (SplashPresenter splashPresenter) { - return splashPresenter; - } - - @PerActivity - @Provides - LoginContract.Presenter provideLoginPresenter - (LoginPresenter loginPresenter) { - return loginPresenter; - } - - @PerActivity - @Provides - MainContract.Presenter provideMainPresenter - (MainPresenter mainPresenter) { - return mainPresenter; - } - - @Provides - BasePagerAdapter provideMainPagerAdapter() { - return new BasePagerAdapter(activity.getSupportFragmentManager()); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java b/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java deleted file mode 100644 index 7c2b3984..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java +++ /dev/null @@ -1,111 +0,0 @@ -package io.github.wulkanowy.di.modules; - -import android.app.Application; -import android.content.Context; - -import com.firebase.jobdispatcher.FirebaseJobDispatcher; -import com.firebase.jobdispatcher.GooglePlayDriver; - -import javax.inject.Singleton; - -import dagger.Module; -import dagger.Provides; -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.data.Repository; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.data.db.dao.DbContract; -import io.github.wulkanowy.data.db.dao.DbHelper; -import io.github.wulkanowy.data.db.dao.DbRepository; -import io.github.wulkanowy.data.db.dao.entities.DaoMaster; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.resources.ResourcesContract; -import io.github.wulkanowy.data.db.resources.ResourcesRepository; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.data.db.shared.SharedPrefRepository; -import io.github.wulkanowy.data.sync.SyncContract; -import io.github.wulkanowy.data.sync.SyncRepository; -import io.github.wulkanowy.di.annotations.ApplicationContext; -import io.github.wulkanowy.di.annotations.DatabaseInfo; -import io.github.wulkanowy.di.annotations.SharedPreferencesInfo; -import io.github.wulkanowy.utils.AppConstant; - -@Module -public class ApplicationModule { - - private final Application application; - - public ApplicationModule(Application application) { - this.application = application; - } - - @Provides - Application provideApplication() { - return application; - } - - @ApplicationContext - @Provides - Context provideAppContext() { - return application; - } - - @DatabaseInfo - @Provides - String provideDatabaseName() { - return AppConstant.DATABASE_NAME; - } - - @SharedPreferencesInfo - @Provides - String provideSharedPreferencesName() { - return AppConstant.SHARED_PREFERENCES_NAME; - } - - @Singleton - @Provides - DaoSession provideDaoSession(DbHelper dbHelper) { - return new DaoMaster(dbHelper.getWritableDb()).newSession(); - } - - @Singleton - @Provides - Vulcan provideVulcan() { - return new Vulcan(); - } - - @Singleton - @Provides - RepositoryContract provideRepository(Repository repository) { - return repository; - } - - @Singleton - @Provides - SharedPrefContract provideSharedPref(SharedPrefRepository sharedPrefRepository) { - return sharedPrefRepository; - } - - @Singleton - @Provides - ResourcesContract provideAppResources(ResourcesRepository resourcesRepository) { - return resourcesRepository; - } - - - @Singleton - @Provides - DbContract provideDatabase(DbRepository dbRepository) { - return dbRepository; - } - - @Singleton - @Provides - SyncContract provideSync(SyncRepository syncRepository) { - return syncRepository; - } - - @Provides - FirebaseJobDispatcher provideDispatcher() { - return new FirebaseJobDispatcher(new GooglePlayDriver(application)); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java b/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java deleted file mode 100644 index 005b7aa1..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java +++ /dev/null @@ -1,103 +0,0 @@ -package io.github.wulkanowy.di.modules; - -import android.support.v4.app.Fragment; - -import dagger.Module; -import dagger.Provides; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import io.github.wulkanowy.di.annotations.PerFragment; -import io.github.wulkanowy.ui.base.BasePagerAdapter; -import io.github.wulkanowy.ui.main.attendance.AttendanceContract; -import io.github.wulkanowy.ui.main.attendance.AttendanceHeaderItem; -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.exams.ExamsContract; -import io.github.wulkanowy.ui.main.exams.ExamsPresenter; -import io.github.wulkanowy.ui.main.exams.ExamsSubItem; -import io.github.wulkanowy.ui.main.exams.ExamsTabContract; -import io.github.wulkanowy.ui.main.exams.ExamsTabPresenter; -import io.github.wulkanowy.ui.main.grades.GradeHeaderItem; -import io.github.wulkanowy.ui.main.grades.GradesContract; -import io.github.wulkanowy.ui.main.grades.GradesPresenter; -import io.github.wulkanowy.ui.main.timetable.TimetableContract; -import io.github.wulkanowy.ui.main.timetable.TimetableHeaderItem; -import io.github.wulkanowy.ui.main.timetable.TimetablePresenter; -import io.github.wulkanowy.ui.main.timetable.TimetableTabContract; -import io.github.wulkanowy.ui.main.timetable.TimetableTabPresenter; - -@Module -public class FragmentModule { - - private final Fragment fragment; - - public FragmentModule(Fragment fragment) { - this.fragment = fragment; - } - - @PerFragment - @Provides - GradesContract.Presenter provideGradesPresenter(GradesPresenter gradesPresenter) { - return gradesPresenter; - } - - @PerFragment - @Provides - AttendanceContract.Presenter provideAttendancePresenter(AttendancePresenter attendancePresenter) { - return attendancePresenter; - } - - @PerFragment - @Provides - ExamsContract.Presenter provideDashboardPresenter(ExamsPresenter examsPresenter) { - return examsPresenter; - } - - @PerFragment - @Provides - AttendanceTabContract.Presenter provideAttendanceTabPresenter(AttendanceTabPresenter timetableTabPresenter) { - return timetableTabPresenter; - } - - @Provides - BasePagerAdapter provideBasePagerAdapter() { - return new BasePagerAdapter(fragment.getChildFragmentManager()); - } - - @Provides - FlexibleAdapter provideAttendanceTabAdapter() { - return new FlexibleAdapter<>(null); - } - - @Provides - FlexibleAdapter provideTimetableTabAdapter() { - return new FlexibleAdapter<>(null); - } - - @Provides - FlexibleAdapter provideGradesAdapter() { - return new FlexibleAdapter<>(null); - } - - @Provides - FlexibleAdapter provideExamAdapter() { - return new FlexibleAdapter<>(null); - } - - @PerFragment - @Provides - TimetableContract.Presenter provideTimetablePresenter(TimetablePresenter timetablePresenter) { - return timetablePresenter; - } - - @PerFragment - @Provides - TimetableTabContract.Presenter provideTimetableTabPresenter(TimetableTabPresenter timetableTabPresenter) { - return timetableTabPresenter; - } - - @Provides - ExamsTabContract.Presenter provideExamsTabPresenter(ExamsTabPresenter examsTabPresenter) { - return examsTabPresenter; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/PerActivity.java b/app/src/main/java/io/github/wulkanowy/di/scopes/PerActivity.java similarity index 81% rename from app/src/main/java/io/github/wulkanowy/di/annotations/PerActivity.java rename to app/src/main/java/io/github/wulkanowy/di/scopes/PerActivity.java index f103994a..c9fc8a5b 100644 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/PerActivity.java +++ b/app/src/main/java/io/github/wulkanowy/di/scopes/PerActivity.java @@ -1,4 +1,4 @@ -package io.github.wulkanowy.di.annotations; +package io.github.wulkanowy.di.scopes; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/DatabaseInfo.java b/app/src/main/java/io/github/wulkanowy/di/scopes/PerChildFragment.java similarity index 51% rename from app/src/main/java/io/github/wulkanowy/di/annotations/DatabaseInfo.java rename to app/src/main/java/io/github/wulkanowy/di/scopes/PerChildFragment.java index fabcefba..67a9f820 100644 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/DatabaseInfo.java +++ b/app/src/main/java/io/github/wulkanowy/di/scopes/PerChildFragment.java @@ -1,11 +1,11 @@ -package io.github.wulkanowy.di.annotations; +package io.github.wulkanowy.di.scopes; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import javax.inject.Qualifier; +import javax.inject.Scope; -@Qualifier +@Scope @Retention(RetentionPolicy.RUNTIME) -public @interface DatabaseInfo { +public @interface PerChildFragment { } diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/PerFragment.java b/app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.java similarity index 81% rename from app/src/main/java/io/github/wulkanowy/di/annotations/PerFragment.java rename to app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.java index 98f364f7..a4d37c38 100644 --- a/app/src/main/java/io/github/wulkanowy/di/annotations/PerFragment.java +++ b/app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.java @@ -1,4 +1,4 @@ -package io.github.wulkanowy.di.annotations; +package io.github.wulkanowy.di.scopes; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/app/src/main/java/io/github/wulkanowy/services/jobs/SyncJob.java b/app/src/main/java/io/github/wulkanowy/services/jobs/SyncJob.java index ab79034f..d0fc73d6 100644 --- a/app/src/main/java/io/github/wulkanowy/services/jobs/SyncJob.java +++ b/app/src/main/java/io/github/wulkanowy/services/jobs/SyncJob.java @@ -4,7 +4,6 @@ import android.app.PendingIntent; import android.content.Context; import android.support.v4.app.NotificationCompat; -import com.crashlytics.android.Crashlytics; import com.firebase.jobdispatcher.Constraint; import com.firebase.jobdispatcher.FirebaseJobDispatcher; import com.firebase.jobdispatcher.GooglePlayDriver; @@ -20,14 +19,16 @@ import java.util.List; import javax.inject.Inject; +import dagger.android.AndroidInjection; import io.github.wulkanowy.R; -import io.github.wulkanowy.WulkanowyApp; +import io.github.wulkanowy.api.login.BadCredentialsException; import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.data.db.dao.entities.Grade; import io.github.wulkanowy.data.sync.NotRegisteredUserException; import io.github.wulkanowy.services.notifies.GradeNotify; import io.github.wulkanowy.ui.main.MainActivity; -import io.github.wulkanowy.utils.LogUtils; +import io.github.wulkanowy.utils.FabricUtils; +import timber.log.Timber; public class SyncJob extends SimpleJobService { @@ -60,7 +61,7 @@ public class SyncJob extends SimpleJobService { @Override public void onCreate() { super.onCreate(); - ((WulkanowyApp) getApplication()).getApplicationComponent().inject(this); + AndroidInjection.inject(this); } @Override @@ -74,13 +75,24 @@ public class SyncJob extends SimpleJobService { if (!gradeList.isEmpty() && repository.getSharedRepo().isNotifyEnable()) { showNotification(); } + + FabricUtils.logLogin("Background", true); + return JobService.RESULT_SUCCESS; } catch (NotRegisteredUserException e) { logError(e); stop(getApplicationContext()); + + return JobService.RESULT_FAIL_NORETRY; + } catch (BadCredentialsException e) { + logError(e); + repository.cleanAllData(); + stop(getApplicationContext()); + return JobService.RESULT_FAIL_NORETRY; } catch (Exception e) { logError(e); + return JobService.RESULT_FAIL_RETRY; } } @@ -122,7 +134,7 @@ public class SyncJob extends SimpleJobService { } private void logError(Exception e) { - Crashlytics.logException(e); - LogUtils.error("During background synchronization an error occurred", e); + FabricUtils.logLogin("Background", false); + Timber.e(e, "During background synchronization an error occurred"); } } diff --git a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetServices.java b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetServices.java index 728cc805..f442542e 100644 --- a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetServices.java +++ b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetServices.java @@ -3,12 +3,20 @@ package io.github.wulkanowy.services.widgets; import android.content.Intent; import android.widget.RemoteViewsService; +import javax.inject.Inject; + +import dagger.android.AndroidInjection; +import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.ui.widgets.TimetableWidgetFactory; public class TimetableWidgetServices extends RemoteViewsService { + @Inject + RepositoryContract repository; + @Override public RemoteViewsFactory onGetViewFactory(Intent intent) { - return new TimetableWidgetFactory(getApplicationContext()); + AndroidInjection.inject(this); + return new TimetableWidgetFactory(getApplicationContext(), repository); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.java b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.java index c5c66df0..82ab9d19 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.java +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.java @@ -1,35 +1,51 @@ package io.github.wulkanowy.ui.base; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; +import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatDelegate; -import android.widget.Toast; +import android.view.View; +import butterknife.ButterKnife; import butterknife.Unbinder; +import dagger.android.support.DaggerAppCompatActivity; import io.github.wulkanowy.R; -import io.github.wulkanowy.WulkanowyApp; -import io.github.wulkanowy.di.component.ActivityComponent; -import io.github.wulkanowy.di.component.DaggerActivityComponent; -import io.github.wulkanowy.di.modules.ActivityModule; import io.github.wulkanowy.utils.NetworkUtils; -public abstract class BaseActivity extends AppCompatActivity implements BaseContract.View { - - private ActivityComponent activityComponent; +public abstract class BaseActivity extends DaggerAppCompatActivity implements BaseContract.View { private Unbinder unbinder; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); + } - activityComponent = DaggerActivityComponent.builder() - .activityModule(new ActivityModule(this)) - .applicationComponent(((WulkanowyApp) getApplication()).getApplicationComponent()) - .build(); + protected void injectViews() { + unbinder = ButterKnife.bind(this); + } + + @Override + public void showMessage(@NonNull String text) { + if (getMessageView() != null) { + Snackbar.make(getMessageView(), text, Snackbar.LENGTH_LONG).show(); + } + } + + @Override + public void showNoNetworkMessage() { + showMessage(getString(R.string.noInternet_text)); + } + + @Override + public boolean isNetworkConnected() { + return NetworkUtils.isOnline(getApplicationContext()); + } + + protected View getMessageView() { + return null; } @Override @@ -38,33 +54,6 @@ public abstract class BaseActivity extends AppCompatActivity implements BaseCont if (unbinder != null) { unbinder.unbind(); } - } - - @Override - public void onError(int resId) { - onError(getString(resId)); - } - - @Override - public void onError(String message) { - Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); - } - - @Override - public void onNoNetworkError() { - onError(R.string.noInternet_text); - } - - @Override - public boolean isNetworkConnected() { - return NetworkUtils.isOnline(getApplicationContext()); - } - - public ActivityComponent getActivityComponent() { - return activityComponent; - } - - public void setButterKnife(Unbinder unbinder) { - this.unbinder = unbinder; + invalidateOptionsMenu(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseContract.java b/app/src/main/java/io/github/wulkanowy/ui/base/BaseContract.java index 2a4dc569..3bfa40d1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseContract.java +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseContract.java @@ -1,27 +1,22 @@ package io.github.wulkanowy.ui.base; -import android.support.annotation.StringRes; - -import io.github.wulkanowy.di.annotations.PerActivity; +import android.support.annotation.NonNull; public interface BaseContract { interface View { - void onError(@StringRes int resId); + void showMessage(@NonNull String text); - void onError(String message); - - void onNoNetworkError(); + void showNoNetworkMessage(); boolean isNetworkConnected(); } - @PerActivity interface Presenter { - void onStart(V view); + void attachView(@NonNull V view); - void onDestroy(); + void detachView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.java b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.java index efb9d61a..e2a5a9e0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.java @@ -1,56 +1,20 @@ package io.github.wulkanowy.ui.base; -import android.content.Context; -import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; +import android.support.annotation.StringRes; import android.view.View; +import butterknife.ButterKnife; import butterknife.Unbinder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.WulkanowyApp; -import io.github.wulkanowy.di.component.DaggerFragmentComponent; -import io.github.wulkanowy.di.component.FragmentComponent; -import io.github.wulkanowy.di.modules.FragmentModule; +import dagger.android.support.DaggerFragment; +import io.github.wulkanowy.utils.NetworkUtils; -public abstract class BaseFragment extends Fragment implements BaseContract.View { - - private BaseActivity activity; +public abstract class BaseFragment extends DaggerFragment implements BaseContract.View { private Unbinder unbinder; - private FragmentComponent fragmentComponent; - - @Override - public void onAttach(Context context) { - super.onAttach(context); - if (context instanceof BaseActivity) { - activity = (BaseActivity) context; - } - - fragmentComponent = DaggerFragmentComponent.builder() - .fragmentModule(new FragmentModule(this)) - .applicationComponent(((WulkanowyApp) activity.getApplication()).getApplicationComponent()) - .build(); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(false); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setUpOnViewCreated(view); - } - - @Override - public void onDetach() { - activity = null; - super.onDetach(); + protected void injectViews(@NonNull View view) { + unbinder = ButterKnife.bind(this, view); } @Override @@ -61,44 +25,32 @@ public abstract class BaseFragment extends Fragment implements BaseContract.View super.onDestroyView(); } - @Override - public void onError(int resId) { - onError(getString(resId)); - } - - @Override - public void onError(String message) { - if (activity != null) { - activity.onError(message); + public void setTitle(String title) { + if (getActivity() != null) { + getActivity().setTitle(title); } } @Override - public void onNoNetworkError() { - onError(R.string.noInternet_text); + public void showMessage(@NonNull String text) { + if (getActivity() != null) { + ((BaseActivity) getActivity()).showMessage(text); + } + } + + public void showMessage(@StringRes int stringId) { + showMessage(getString(stringId)); + } + + @Override + public void showNoNetworkMessage() { + if (getActivity() != null) { + ((BaseActivity) getActivity()).showNoNetworkMessage(); + } } @Override public boolean isNetworkConnected() { - return activity != null && activity.isNetworkConnected(); - } - - public void setButterKnife(Unbinder unbinder) { - this.unbinder = unbinder; - } - - public void setTitle(String title) { - if (activity != null) { - activity.setTitle(title); - } - } - - public FragmentComponent getFragmentComponent() { - return fragmentComponent; - } - - - protected void setUpOnViewCreated(View fragmentView) { - // do something on view created + return NetworkUtils.isOnline(getContext()); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.java b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.java index 611b7a29..8f9b8af0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.java @@ -1,5 +1,7 @@ package io.github.wulkanowy.ui.base; +import android.support.annotation.NonNull; + import javax.inject.Inject; import io.github.wulkanowy.data.RepositoryContract; @@ -16,12 +18,12 @@ public class BasePresenter implements BaseContract. } @Override - public void onStart(V view) { + public void attachView(@NonNull V view) { this.view = view; } @Override - public void onDestroy() { + public void detachView() { view = null; } diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java index 9d6e8b12..a0960af2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java @@ -5,7 +5,7 @@ import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.support.design.widget.Snackbar; +import android.support.annotation.NonNull; import android.support.design.widget.TextInputLayout; import android.support.v7.app.ActionBar; import android.view.View; @@ -14,11 +14,11 @@ import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.EditText; import android.widget.TextView; +import android.widget.Toast; import javax.inject.Inject; import butterknife.BindView; -import butterknife.ButterKnife; import butterknife.OnClick; import butterknife.OnEditorAction; import io.github.wulkanowy.R; @@ -64,14 +64,10 @@ public class LoginActivity extends BaseActivity implements LoginContract.View { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); + injectViews(); - setButterKnife(ButterKnife.bind(this)); - getActivityComponent().inject(this); - - presenter.onStart(this); - + presenter.attachView(this); setUpOnCreate(); - } protected void setUpOnCreate() { @@ -165,12 +161,6 @@ public class LoginActivity extends BaseActivity implements LoginContract.View { KeyboardUtils.hideSoftInput(this); } - @Override - public void onError(String message) { - Snackbar.make(findViewById(R.id.login_activity_container), message, - Snackbar.LENGTH_LONG).show(); - } - @Override public void setStepOneLoginProgress() { onLoginProgressUpdate("1", getString(R.string.step_login)); @@ -222,13 +212,25 @@ public class LoginActivity extends BaseActivity implements LoginContract.View { } } + @NonNull @Override - public void onDestroy() { - super.onDestroy(); - presenter.onDestroy(); + protected View getMessageView() { + return findViewById(R.id.login_activity_container); + } + + + @Override + public void onSyncFailed() { + Toast.makeText(getApplicationContext(), R.string.login_sync_error, Toast.LENGTH_LONG).show(); } private void onLoginProgressUpdate(String step, String message) { loginProgressText.setText(String.format("%1$s/2 - %2$s...", step, message)); } + + @Override + public void onDestroy() { + presenter.detachView(); + super.onDestroy(); + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginContract.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginContract.java index 570e1b26..96bb49d6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/login/LoginContract.java +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginContract.java @@ -1,7 +1,5 @@ package io.github.wulkanowy.ui.login; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.di.annotations.PerActivity; import io.github.wulkanowy.ui.base.BaseContract; public interface LoginContract { @@ -35,9 +33,10 @@ public interface LoginContract { void showActionBar(boolean show); + void onSyncFailed(); + } - @PerActivity interface Presenter extends BaseContract.Presenter { void attemptLogin(String email, String password, String symbol); @@ -48,10 +47,8 @@ public interface LoginContract { void onLoginProgress(int step); - void onEndAsync(boolean success, Exception exception); + void onEndAsync(int success, Exception exception); void onCanceledAsync(); - - RepositoryContract getRepository(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginModule.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginModule.java new file mode 100644 index 00000000..0cb26c49 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginModule.java @@ -0,0 +1,13 @@ +package io.github.wulkanowy.ui.login; + +import dagger.Binds; +import dagger.Module; +import io.github.wulkanowy.di.scopes.PerActivity; + +@Module +public abstract class LoginModule { + + @PerActivity + @Binds + abstract LoginContract.Presenter provideLoginPresenter(LoginPresenter loginPresenter); +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginPresenter.java index e5b37a6e..d8461bf3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/login/LoginPresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginPresenter.java @@ -47,7 +47,7 @@ public class LoginPresenter extends BasePresenter loginAsync.execute(); } else { - getView().onNoNetworkError(); + getView().showNoNetworkMessage(); } getView().hideSoftInput(); @@ -83,22 +83,30 @@ public class LoginPresenter extends BasePresenter } @Override - public void onEndAsync(boolean success, Exception exception) { - if (success) { - FabricUtils.logRegister(true, getRepository().getDbRepo().getCurrentSymbol().getSymbol()); - getView().openMainActivity(); - return; - } else if (exception instanceof BadCredentialsException) { - getView().setErrorPassIncorrect(); - getView().showSoftInput(); - } else if (exception instanceof AccountPermissionException) { - getView().setErrorSymbolRequired(); - getView().showSoftInput(); - } else { - FabricUtils.logRegister(false, symbol); - getView().onError(getRepository().getResRepo().getErrorLoginMessage(exception)); + public void onEndAsync(int success, Exception exception) { + switch (success) { + case LoginTask.LOGIN_AND_SYNC_SUCCESS: + FabricUtils.logRegister(true, getRepository().getDbRepo().getCurrentSymbol().getSymbol(), "Success"); + getView().openMainActivity(); + return; + case LoginTask.SYNC_FAILED: + FabricUtils.logRegister(true, symbol, exception.getMessage()); + getView().onSyncFailed(); + getView().openMainActivity(); + return; + case LoginTask.LOGIN_FAILED: + if (exception instanceof BadCredentialsException) { + getView().setErrorPassIncorrect(); + getView().showSoftInput(); + } else if (exception instanceof AccountPermissionException) { + getView().setErrorSymbolRequired(); + getView().showSoftInput(); + } else { + FabricUtils.logRegister(false, symbol, exception.getMessage()); + getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception)); + } + break; } - getView().showActionBar(true); getView().showLoginProgress(false); } @@ -160,11 +168,11 @@ public class LoginPresenter extends BasePresenter } @Override - public void onDestroy() { + public void detachView() { if (loginAsync != null) { loginAsync.cancel(true); loginAsync = null; } - super.onDestroy(); + super.detachView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginTask.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginTask.java index b22194da..2938a836 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/login/LoginTask.java +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginTask.java @@ -2,7 +2,13 @@ package io.github.wulkanowy.ui.login; import android.os.AsyncTask; -public class LoginTask extends AsyncTask { +public class LoginTask extends AsyncTask { + + public final static int LOGIN_AND_SYNC_SUCCESS = 1; + + public final static int LOGIN_FAILED = -1; + + public final static int SYNC_FAILED = 2; private LoginContract.Presenter presenter; @@ -18,18 +24,23 @@ public class LoginTask extends AsyncTask { } @Override - protected Boolean doInBackground(Void... params) { + protected Integer doInBackground(Void... params) { try { publishProgress(1); presenter.onDoInBackground(1); + } catch (Exception e) { + exception = e; + return LOGIN_FAILED; + } + try { publishProgress(2); presenter.onDoInBackground(2); } catch (Exception e) { exception = e; - return false; + return SYNC_FAILED; } - return true; + return LOGIN_AND_SYNC_SUCCESS; } @Override @@ -38,7 +49,7 @@ public class LoginTask extends AsyncTask { } @Override - protected void onPostExecute(Boolean success) { + protected void onPostExecute(Integer success) { presenter.onEndAsync(success, exception); } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java index 6b7d07cf..b739ae98 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java @@ -2,8 +2,9 @@ package io.github.wulkanowy.ui.main; import android.content.Context; import android.content.Intent; -import android.graphics.Color; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.design.widget.AppBarLayout; import android.support.v7.app.ActionBar; import android.support.v7.widget.Toolbar; import android.view.View; @@ -13,10 +14,11 @@ import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem; import com.aurelhubert.ahbottomnavigation.AHBottomNavigationViewPager; import javax.inject.Inject; +import javax.inject.Named; import butterknife.BindView; -import butterknife.ButterKnife; import io.github.wulkanowy.R; +import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.services.jobs.SyncJob; import io.github.wulkanowy.ui.base.BaseActivity; import io.github.wulkanowy.ui.base.BasePagerAdapter; @@ -25,6 +27,7 @@ import io.github.wulkanowy.ui.main.exams.ExamsFragment; import io.github.wulkanowy.ui.main.grades.GradesFragment; import io.github.wulkanowy.ui.main.settings.SettingsFragment; import io.github.wulkanowy.ui.main.timetable.TimetableFragment; +import io.github.wulkanowy.utils.CommonUtils; public class MainActivity extends BaseActivity implements MainContract.View, AHBottomNavigation.OnTabSelectedListener, OnFragmentIsReadyListener { @@ -40,12 +43,19 @@ public class MainActivity extends BaseActivity implements MainContract.View, @BindView(R.id.main_activity_progress_bar) View progressBar; + @BindView(R.id.main_activity_appbar) + AppBarLayout appBar; + + @Named("Main") @Inject BasePagerAdapter pagerAdapter; @Inject MainContract.Presenter presenter; + @Inject + RepositoryContract repository; + public static Intent getStartIntent(Context context) { return new Intent(context, MainActivity.class); } @@ -54,12 +64,10 @@ public class MainActivity extends BaseActivity implements MainContract.View, public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + setSupportActionBar((Toolbar) findViewById(R.id.main_activity_toolbar)); + injectViews(); - getActivityComponent().inject(this); - setButterKnife(ButterKnife.bind(this)); - - presenter.onStart(this, getIntent().getIntExtra(EXTRA_CARD_ID_KEY, -1)); + presenter.attachView(this, getIntent().getIntExtra(EXTRA_CARD_ID_KEY, -1)); } @Override @@ -88,6 +96,8 @@ public class MainActivity extends BaseActivity implements MainContract.View, @Override public boolean onTabSelected(int position, boolean wasSelected) { presenter.onTabSelected(position, wasSelected); + appBar.setExpanded(true, true); + invalidateOptionsMenu(); return true; } @@ -119,8 +129,8 @@ public class MainActivity extends BaseActivity implements MainContract.View, R.drawable.ic_menu_other_24dp)); bottomNavigation.setAccentColor(getResources().getColor(R.color.colorPrimary)); - bottomNavigation.setInactiveColor(Color.BLACK); - bottomNavigation.setBackgroundColor(getResources().getColor(R.color.colorBackgroundBottomNav)); + bottomNavigation.setInactiveColor(CommonUtils.getThemeAttrColor(this, android.R.attr.textColorTertiary)); + bottomNavigation.setDefaultBackgroundColor(CommonUtils.getThemeAttrColor(this, R.attr.bottomNavBackground)); bottomNavigation.setTitleState(AHBottomNavigation.TitleState.ALWAYS_SHOW); bottomNavigation.setOnTabSelectedListener(this); bottomNavigation.setCurrentItem(tabPosition); @@ -146,9 +156,15 @@ public class MainActivity extends BaseActivity implements MainContract.View, SyncJob.start(getApplicationContext(), interval, useOnlyWifi); } + @NonNull + @Override + protected View getMessageView() { + return findViewById(R.id.main_activity_view_pager); + } + @Override protected void onDestroy() { + presenter.detachView(); super.onDestroy(); - presenter.onDestroy(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainContract.java index cc326654..cb4e29e4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/MainContract.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainContract.java @@ -1,6 +1,7 @@ package io.github.wulkanowy.ui.main; -import io.github.wulkanowy.di.annotations.PerActivity; +import android.support.annotation.NonNull; + import io.github.wulkanowy.ui.base.BaseContract; public interface MainContract { @@ -22,10 +23,9 @@ public interface MainContract { void startSyncService(int interval, boolean useOnlyWifi); } - @PerActivity interface Presenter extends BaseContract.Presenter { - void onStart(View view, int tabPositionIntent); + void attachView(@NonNull View view, int initTabId); void onTabSelected(int position, boolean wasSelected); diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainModule.java new file mode 100644 index 00000000..ff3fed67 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainModule.java @@ -0,0 +1,50 @@ +package io.github.wulkanowy.ui.main; + +import javax.inject.Named; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import dagger.android.ContributesAndroidInjector; +import io.github.wulkanowy.di.scopes.PerActivity; +import io.github.wulkanowy.di.scopes.PerFragment; +import io.github.wulkanowy.ui.base.BasePagerAdapter; +import io.github.wulkanowy.ui.main.attendance.AttendanceFragment; +import io.github.wulkanowy.ui.main.attendance.AttendanceModule; +import io.github.wulkanowy.ui.main.exams.ExamsFragment; +import io.github.wulkanowy.ui.main.exams.ExamsModule; +import io.github.wulkanowy.ui.main.grades.GradesFragment; +import io.github.wulkanowy.ui.main.grades.GradesModule; +import io.github.wulkanowy.ui.main.timetable.TimetableFragment; +import io.github.wulkanowy.ui.main.timetable.TimetableModule; + +@Module +public abstract class MainModule { + + @PerActivity + @Binds + abstract MainContract.Presenter provideMainPresenter(MainPresenter mainPresenter); + + @Named("Main") + @PerActivity + @Provides + static BasePagerAdapter provideAdapter(MainActivity activity) { + return new BasePagerAdapter(activity.getSupportFragmentManager()); + } + + @PerFragment + @ContributesAndroidInjector(modules = GradesModule.class) + abstract GradesFragment bindsGradesFragment(); + + @PerFragment + @ContributesAndroidInjector(modules = TimetableModule.class) + abstract TimetableFragment bindTimetableFragment(); + + @PerFragment + @ContributesAndroidInjector(modules = ExamsModule.class) + abstract ExamsFragment bindExamsFragment(); + + @PerFragment + @ContributesAndroidInjector(modules = AttendanceModule.class) + abstract AttendanceFragment bindAttendanceFramgnet(); +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainPresenter.java index ba5e41d8..c6741c3e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/MainPresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainPresenter.java @@ -1,6 +1,8 @@ package io.github.wulkanowy.ui.main; +import android.support.annotation.NonNull; + import javax.inject.Inject; import io.github.wulkanowy.data.RepositoryContract; @@ -17,15 +19,15 @@ public class MainPresenter extends BasePresenter } @Override - public void onStart(MainContract.View view, int tabPositionIntent) { - super.onStart(view); + public void attachView(@NonNull MainContract.View view, int initTabId) { + super.attachView(view); getView().showProgressBar(true); getView().hideActionBar(); int tabPosition; - if (tabPositionIntent != -1) { - tabPosition = tabPositionIntent; + if (initTabId != -1) { + tabPosition = initTabId; } else { tabPosition = getRepository().getSharedRepo().getStartupTab(); } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java index daee63d2..1ad66787 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java @@ -1,6 +1,7 @@ package io.github.wulkanowy.ui.main.attendance; -import io.github.wulkanowy.di.annotations.PerActivity; +import android.support.annotation.NonNull; + import io.github.wulkanowy.ui.base.BaseContract; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; @@ -21,12 +22,11 @@ public interface AttendanceContract { void setThemeForTab(int position); } - @PerActivity interface Presenter extends BaseContract.Presenter { void onFragmentActivated(boolean isVisible); - void onStart(View view, OnFragmentIsReadyListener listener); + void attachView(@NonNull View view, OnFragmentIsReadyListener listener); void setRestoredPosition(int position); } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java index 5e8f3a02..ea38e51d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java @@ -78,7 +78,7 @@ public class AttendanceDialogFragment extends DialogFragment { description.setText(lesson.getDescription()); if (lesson.getAbsenceUnexcused()) { - description.setTextColor(getResources().getColor(R.color.colorPrimaryDark)); + description.setTextColor(getResources().getColor(R.color.colorPrimary)); } return view; diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java index e26c9fc7..732806db 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java @@ -3,7 +3,6 @@ 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; @@ -11,14 +10,14 @@ import android.view.View; import android.view.ViewGroup; import javax.inject.Inject; +import javax.inject.Named; 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.base.BasePagerAdapter; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; +import io.github.wulkanowy.ui.main.attendance.tab.AttendanceTabFragment; public class AttendanceFragment extends BaseFragment implements AttendanceContract.View { @@ -31,6 +30,7 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra TabLayout tabLayout; @Inject + @Named("Attendance") BasePagerAdapter pagerAdapter; @Inject @@ -41,18 +41,13 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_attendance, container, false); + injectViews(view); - FragmentComponent component = getFragmentComponent(); - if (component != null) { - component.inject(this); - setButterKnife(ButterKnife.bind(this, view)); - presenter.onStart(this, (OnFragmentIsReadyListener) getActivity()); + presenter.attachView(this, (OnFragmentIsReadyListener) getActivity()); - if (savedInstanceState != null) { - presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY)); - } + if (savedInstanceState != null) { + presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY)); } - return view; } @@ -93,14 +88,6 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra 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 onSaveInstanceState(Bundle outState) { outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem()); @@ -109,7 +96,7 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra @Override public void onDestroyView() { - presenter.onDestroy(); + presenter.detachView(); super.onDestroyView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceModule.java new file mode 100644 index 00000000..8679cf10 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceModule.java @@ -0,0 +1,32 @@ +package io.github.wulkanowy.ui.main.attendance; + +import javax.inject.Named; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import dagger.android.ContributesAndroidInjector; +import io.github.wulkanowy.di.scopes.PerChildFragment; +import io.github.wulkanowy.di.scopes.PerFragment; +import io.github.wulkanowy.ui.base.BasePagerAdapter; +import io.github.wulkanowy.ui.main.attendance.tab.AttendanceTabFragment; +import io.github.wulkanowy.ui.main.attendance.tab.AttendanceTabModule; + +@Module +public abstract class AttendanceModule { + + @PerFragment + @Binds + abstract AttendanceContract.Presenter provideAttendancePresenter(AttendancePresenter attendancePresenter); + + @PerFragment + @Named("Attendance") + @Provides + static BasePagerAdapter providePagerAdapter(AttendanceFragment fragment) { + return new BasePagerAdapter(fragment.getChildFragmentManager()); + } + + @PerChildFragment + @ContributesAndroidInjector(modules = AttendanceTabModule.class) + abstract AttendanceTabFragment bindAttendanceTabFragment(); +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java index 55541ed2..9cfb1384 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java @@ -1,5 +1,7 @@ package io.github.wulkanowy.ui.main.attendance; +import android.support.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -31,8 +33,8 @@ public class AttendancePresenter extends BasePresenter } @Override - public void onStart(AttendanceContract.View view, OnFragmentIsReadyListener listener) { - super.onStart(view); + public void attachView(@NonNull AttendanceContract.View view, OnFragmentIsReadyListener listener) { + super.attachView(view); this.listener = listener; if (getView().isMenuVisible()) { @@ -64,7 +66,7 @@ public class AttendancePresenter extends BasePresenter } @Override - public void onDoInBackgroundLoading() throws Exception { + public void onDoInBackgroundLoading() { for (String date : dates) { getView().setTabDataToAdapter(date); } @@ -92,14 +94,13 @@ public class AttendancePresenter extends BasePresenter } @Override - public void onDestroy() { + public void detachView() { isFirstSight = false; if (loadingTask != null) { loadingTask.cancel(true); loadingTask = null; } - - super.onDestroy(); + super.detachView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceHeader.java similarity index 86% rename from app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java rename to app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceHeader.java index b7e6aecd..86128109 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceHeader.java @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.main.attendance; +package io.github.wulkanowy.ui.main.attendance.tab; import android.content.Context; import android.content.res.TypedArray; @@ -14,7 +14,6 @@ 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; @@ -23,13 +22,14 @@ import eu.davidea.flexibleadapter.items.IFlexible; import eu.davidea.viewholders.ExpandableViewHolder; import io.github.wulkanowy.R; import io.github.wulkanowy.data.db.dao.entities.Day; +import io.github.wulkanowy.utils.CommonUtils; -public class AttendanceHeaderItem - extends AbstractExpandableHeaderItem { +public class AttendanceHeader + extends AbstractExpandableHeaderItem { private Day day; - AttendanceHeaderItem(Day day) { + AttendanceHeader(Day day) { this.day = day; } @@ -39,7 +39,7 @@ public class AttendanceHeaderItem if (o == null || getClass() != o.getClass()) return false; - AttendanceHeaderItem that = (AttendanceHeaderItem) o; + AttendanceHeader that = (AttendanceHeader) o; return new EqualsBuilder() .append(day, that.day) @@ -86,15 +86,6 @@ public class AttendanceHeaderItem @BindView(R.id.attendance_header_free_name) TextView freeName; - @BindColor(R.color.secondary_text) - int secondaryColor; - - @BindColor(R.color.free_day) - int backgroundFreeDay; - - @BindColor(android.R.color.black) - int black; - private Context context; HeaderViewHolder(View view, FlexibleAdapter adapter) { @@ -117,16 +108,15 @@ public class AttendanceHeaderItem setInactiveHeader(item.getAttendanceLessons().isEmpty()); } - private void setInactiveHeader(boolean inactive) { ((FrameLayout) getContentView()).setForeground(inactive ? null : getSelectableDrawable()); - dayName.setTextColor(inactive ? secondaryColor : black); + dayName.setTextColor(CommonUtils.getThemeAttrColor(context, + inactive ? android.R.attr.textColorSecondary : android.R.attr.textColorPrimary)); if (inactive) { - getContentView().setBackgroundColor(backgroundFreeDay); + getContentView().setBackgroundColor(CommonUtils.getThemeAttrColor(context, R.attr.colorControlHighlight)); } else { - getContentView().setBackgroundDrawable(context.getResources() - .getDrawable(R.drawable.ic_border)); + getContentView().setBackgroundDrawable(context.getResources().getDrawable(R.drawable.ic_border)); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceSubItem.java similarity index 93% rename from app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java rename to app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceSubItem.java index 440d878e..65c8329e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceSubItem.java @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.main.attendance; +package io.github.wulkanowy.ui.main.attendance.tab; import android.content.Context; import android.support.v4.app.DialogFragment; @@ -20,13 +20,14 @@ import eu.davidea.flexibleadapter.items.IFlexible; import eu.davidea.viewholders.FlexibleViewHolder; import io.github.wulkanowy.R; import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; +import io.github.wulkanowy.ui.main.attendance.AttendanceDialogFragment; class AttendanceSubItem - extends AbstractSectionableItem { + extends AbstractSectionableItem { private AttendanceLesson lesson; - AttendanceSubItem(AttendanceHeaderItem header, AttendanceLesson lesson) { + AttendanceSubItem(AttendanceHeader header, AttendanceLesson lesson) { super(header); this.lesson = lesson; } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabContract.java similarity index 81% rename from app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java rename to app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabContract.java index dcb16bf8..a83e67e8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabContract.java @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.main.attendance; +package io.github.wulkanowy.ui.main.attendance.tab; import java.util.List; @@ -8,7 +8,7 @@ public interface AttendanceTabContract { interface View extends BaseContract.View { - void updateAdapterList(List headerItems); + void updateAdapterList(List headerItems); void onRefreshSuccess(); diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabFragment.java similarity index 73% rename from app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java rename to app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabFragment.java index e5e65bb9..8de47469 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabFragment.java @@ -1,9 +1,8 @@ -package io.github.wulkanowy.ui.main.attendance; +package io.github.wulkanowy.ui.main.attendance.tab; 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; @@ -15,11 +14,9 @@ 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, @@ -43,7 +40,7 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab AttendanceTabContract.Presenter presenter; @Inject - FlexibleAdapter adapter; + FlexibleAdapter adapter; private boolean isFragmentVisible = false; @@ -61,38 +58,32 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_attendance_tab, container, false); + injectViews(view); - 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); - presenter.onFragmentActivated(isFragmentVisible); + if (getArguments() != null) { + presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY)); } + + presenter.attachView(this); + presenter.onFragmentActivated(isFragmentVisible); return view; } @Override - protected void setUpOnViewCreated(View fragmentView) { + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { adapter.setAutoCollapseOnExpand(true); adapter.setAutoScrollOnExpand(true); adapter.expandItemsAtStartUp(); - recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext())); + recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext())); recyclerView.setAdapter(adapter); refreshLayout.setColorSchemeResources(android.R.color.black); refreshLayout.setOnRefreshListener(this); - } @Override - public void updateAdapterList(List headerItems) { + public void updateAdapterList(List headerItems) { adapter.updateDataSet(headerItems); } @@ -112,7 +103,7 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab @Override public void onRefreshSuccess() { - onError(R.string.sync_completed); + showMessage(R.string.sync_completed); } @Override @@ -130,17 +121,9 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); } - @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() { - presenter.onDestroy(); + presenter.detachView(); super.onDestroyView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabModule.java new file mode 100644 index 00000000..380a221f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabModule.java @@ -0,0 +1,21 @@ +package io.github.wulkanowy.ui.main.attendance.tab; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import io.github.wulkanowy.di.scopes.PerChildFragment; + +@Module +public abstract class AttendanceTabModule { + + @PerChildFragment + @Binds + abstract AttendanceTabContract.Presenter provideAttendanceTabPresenter(AttendanceTabPresenter attendanceTabPresenter); + + @PerChildFragment + @Provides + static FlexibleAdapter provideAdapter() { + return new FlexibleAdapter<>(null); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabPresenter.java similarity index 90% rename from app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java rename to app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabPresenter.java index 72cab5d3..7d0c26e5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabPresenter.java @@ -1,4 +1,6 @@ -package io.github.wulkanowy.ui.main.attendance; +package io.github.wulkanowy.ui.main.attendance.tab; + +import android.support.annotation.NonNull; import java.util.ArrayList; import java.util.List; @@ -22,7 +24,7 @@ public class AttendanceTabPresenter extends BasePresenter headerItems = new ArrayList<>(); + private List headerItems = new ArrayList<>(); private String date; @@ -34,8 +36,9 @@ public class AttendanceTabPresenter extends BasePresenter { - void onStart(View view, OnFragmentIsReadyListener listener); + void attachView(@NonNull View view, OnFragmentIsReadyListener listener); void onFragmentActivated(boolean isVisible); diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsFragment.java index cab3b902..05557b81 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsFragment.java @@ -2,7 +2,6 @@ package io.github.wulkanowy.ui.main.exams; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.design.widget.Snackbar; import android.support.design.widget.TabLayout; import android.support.v4.view.ViewPager; import android.view.LayoutInflater; @@ -10,14 +9,14 @@ import android.view.View; import android.view.ViewGroup; import javax.inject.Inject; +import javax.inject.Named; 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.base.BasePagerAdapter; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; +import io.github.wulkanowy.ui.main.exams.tab.ExamsTabFragment; public class ExamsFragment extends BaseFragment implements ExamsContract.View { @@ -30,6 +29,7 @@ public class ExamsFragment extends BaseFragment implements ExamsContract.View { TabLayout tabLayout; @Inject + @Named("Exams") BasePagerAdapter pagerAdapter; @Inject @@ -39,16 +39,12 @@ public class ExamsFragment extends BaseFragment implements ExamsContract.View { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_exams, container, false); + injectViews(view); - FragmentComponent component = getFragmentComponent(); - if (component != null) { - component.inject(this); - setButterKnife(ButterKnife.bind(this, view)); - presenter.onStart(this, (OnFragmentIsReadyListener) getActivity()); + presenter.attachView(this, (OnFragmentIsReadyListener) getActivity()); - if (savedInstanceState != null) { - presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY)); - } + if (savedInstanceState != null) { + presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY)); } return view; } @@ -61,14 +57,6 @@ public class ExamsFragment extends BaseFragment implements ExamsContract.View { } } - @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 setActivityTitle() { setTitle(getString(R.string.exams_text)); @@ -107,6 +95,6 @@ public class ExamsFragment extends BaseFragment implements ExamsContract.View { @Override public void onDestroyView() { super.onDestroyView(); - presenter.onDestroy(); + presenter.detachView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsModule.java new file mode 100644 index 00000000..8d56cf23 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsModule.java @@ -0,0 +1,32 @@ +package io.github.wulkanowy.ui.main.exams; + +import javax.inject.Named; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import dagger.android.ContributesAndroidInjector; +import io.github.wulkanowy.di.scopes.PerChildFragment; +import io.github.wulkanowy.di.scopes.PerFragment; +import io.github.wulkanowy.ui.base.BasePagerAdapter; +import io.github.wulkanowy.ui.main.exams.tab.ExamsTabFragment; +import io.github.wulkanowy.ui.main.exams.tab.ExamsTabModule; + +@Module +public abstract class ExamsModule { + + @PerFragment + @Binds + abstract ExamsContract.Presenter provideExamsPresneter(ExamsPresenter examsPresenter); + + @Named("Exams") + @PerFragment + @Provides + static BasePagerAdapter providePagerAdapter(ExamsFragment fragment) { + return new BasePagerAdapter(fragment.getChildFragmentManager()); + } + + @PerChildFragment + @ContributesAndroidInjector(modules = ExamsTabModule.class) + abstract ExamsTabFragment bindExamsTabFragment(); +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsPresenter.java index 5f6cd4dc..a5bd1055 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsPresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsPresenter.java @@ -1,5 +1,7 @@ package io.github.wulkanowy.ui.main.exams; +import android.support.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -31,8 +33,8 @@ public class ExamsPresenter extends BasePresenter } @Override - public void onStart(ExamsContract.View view, OnFragmentIsReadyListener listener) { - super.onStart(view); + public void attachView(@NonNull ExamsContract.View view, OnFragmentIsReadyListener listener) { + super.attachView(view); this.listener = listener; if (getView().isMenuVisible()) { @@ -69,7 +71,7 @@ public class ExamsPresenter extends BasePresenter } @Override - public void onDoInBackgroundLoading() throws Exception { + public void onDoInBackgroundLoading() { for (String date : dates) { getView().setTabDataToAdapter(date); } @@ -91,13 +93,13 @@ public class ExamsPresenter extends BasePresenter } @Override - public void onDestroy() { + public void detachView() { isFirstSight = false; if (loadingTask != null) { loadingTask.cancel(true); loadingTask = null; } - super.onDestroy(); + super.detachView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsHeader.java similarity index 89% rename from app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsHeaderItem.java rename to app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsHeader.java index 86070515..6c0c364e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsHeaderItem.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsHeader.java @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.main.exams; +package io.github.wulkanowy.ui.main.exams.tab; import android.view.View; import android.widget.TextView; @@ -18,11 +18,11 @@ import eu.davidea.viewholders.FlexibleViewHolder; import io.github.wulkanowy.R; import io.github.wulkanowy.data.db.dao.entities.Day; -public class ExamsHeaderItem extends AbstractHeaderItem { +public class ExamsHeader extends AbstractHeaderItem { private Day day; - public ExamsHeaderItem(Day day) { + ExamsHeader(Day day) { this.day = day; } @@ -32,7 +32,7 @@ public class ExamsHeaderItem extends AbstractHeaderItem { + extends AbstractSectionableItem { private Exam exam; - ExamsSubItem(ExamsHeaderItem header, Exam exam) { + ExamsSubItem(ExamsHeader header, Exam exam) { super(header); this.exam = exam; } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsTabContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabContract.java similarity index 83% rename from app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsTabContract.java rename to app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabContract.java index ec59628a..4e0785bb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsTabContract.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabContract.java @@ -1,8 +1,7 @@ -package io.github.wulkanowy.ui.main.exams; +package io.github.wulkanowy.ui.main.exams.tab; import java.util.List; -import io.github.wulkanowy.di.annotations.PerFragment; import io.github.wulkanowy.ui.base.BaseContract; public interface ExamsTabContract { @@ -20,7 +19,6 @@ public interface ExamsTabContract { void updateAdapterList(List headerItems); } - @PerFragment interface Presenter extends BaseContract.Presenter { void onFragmentActivated(boolean isSelected); diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsTabFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabFragment.java similarity index 74% rename from app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsTabFragment.java rename to app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabFragment.java index d41e34d2..1b6f361a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsTabFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabFragment.java @@ -1,8 +1,7 @@ -package io.github.wulkanowy.ui.main.exams; +package io.github.wulkanowy.ui.main.exams.tab; import android.os.Bundle; 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; @@ -14,11 +13,9 @@ 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 ExamsTabFragment extends BaseFragment implements ExamsTabContract.View, @@ -60,26 +57,21 @@ public class ExamsTabFragment extends BaseFragment implements ExamsTabContract.V @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_exams_tab, container, false); + injectViews(view); - 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); - presenter.onFragmentActivated(isFragmentVisible); + if (getArguments() != null) { + presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY)); } + presenter.attachView(this); + presenter.onFragmentActivated(isFragmentVisible); return view; } @Override - protected void setUpOnViewCreated(View fragmentView) { + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { adapter.setDisplayHeadersAtStartUp(true); - recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext())); + recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext())); recyclerView.setAdapter(adapter); refreshLayout.setColorSchemeResources(android.R.color.black); @@ -107,7 +99,7 @@ public class ExamsTabFragment extends BaseFragment implements ExamsTabContract.V @Override public void onRefreshSuccess() { - onError(R.string.sync_completed); + showMessage(R.string.sync_completed); } @Override @@ -125,17 +117,10 @@ public class ExamsTabFragment extends BaseFragment implements ExamsTabContract.V progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE); } - @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() { - presenter.onDestroy(); + presenter.detachView(); super.onDestroyView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabModule.java new file mode 100644 index 00000000..fc9a7595 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabModule.java @@ -0,0 +1,21 @@ +package io.github.wulkanowy.ui.main.exams.tab; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import io.github.wulkanowy.di.scopes.PerChildFragment; + +@Module +public abstract class ExamsTabModule { + + @PerChildFragment + @Binds + abstract ExamsTabContract.Presenter provideExamsTabPresenter(ExamsTabPresenter examsTabPresenter); + + @PerChildFragment + @Provides + static FlexibleAdapter provideAdapter() { + return new FlexibleAdapter<>(null); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabPresenter.java similarity index 89% rename from app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsTabPresenter.java rename to app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabPresenter.java index 5925b357..b60a4607 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsTabPresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabPresenter.java @@ -1,4 +1,6 @@ -package io.github.wulkanowy.ui.main.exams; +package io.github.wulkanowy.ui.main.exams.tab; + +import android.support.annotation.NonNull; import java.util.ArrayList; import java.util.List; @@ -29,13 +31,13 @@ public class ExamsTabPresenter extends BasePresenter private boolean isFirstSight = false; @Inject - public ExamsTabPresenter(RepositoryContract repository) { + ExamsTabPresenter(RepositoryContract repository) { super(repository); } @Override - public void onStart(ExamsTabContract.View view) { - super.onStart(view); + public void attachView(@NonNull ExamsTabContract.View view) { + super.attachView(view); getView().showProgressBar(true); getView().showNoItem(false); } @@ -65,7 +67,7 @@ public class ExamsTabPresenter extends BasePresenter refreshTask.setOnRefreshListener(this); refreshTask.execute(); } else { - getView().onNoNetworkError(); + getView().showNoNetworkMessage(); getView().hideRefreshingBar(); } } @@ -91,7 +93,7 @@ public class ExamsTabPresenter extends BasePresenter getView().onRefreshSuccess(); } else { - getView().onError(getRepository().getResRepo().getErrorLoginMessage(exception)); + getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception)); } getView().hideRefreshingBar(); @@ -114,7 +116,7 @@ public class ExamsTabPresenter extends BasePresenter for (Day day : dayList) { day.resetExams(); - ExamsHeaderItem headerItem = new ExamsHeaderItem(day); + ExamsHeader headerItem = new ExamsHeader(day); List examList = day.getExams(); @@ -157,9 +159,9 @@ public class ExamsTabPresenter extends BasePresenter } @Override - public void onDestroy() { + public void detachView() { isFirstSight = false; cancelAsyncTasks(); - super.onDestroy(); + super.detachView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesContract.java index 07ca0d73..d2423ec8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesContract.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesContract.java @@ -1,10 +1,10 @@ package io.github.wulkanowy.ui.main.grades; +import android.support.annotation.NonNull; import android.support.v4.widget.SwipeRefreshLayout; import java.util.List; -import io.github.wulkanowy.di.annotations.PerActivity; import io.github.wulkanowy.ui.base.BaseContract; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; @@ -12,7 +12,9 @@ public interface GradesContract { interface View extends BaseContract.View, SwipeRefreshLayout.OnRefreshListener { - void updateAdapterList(List headerItems); + void updateAdapterList(List headerItems); + + void updateSummaryAdapterList(List summarySubItems); void showNoItem(boolean show); @@ -28,16 +30,17 @@ public interface GradesContract { boolean isMenuVisible(); + void setSummaryAverages(String calculatedValue, String predictedValue, String finalValue ); + } - @PerActivity interface Presenter extends BaseContract.Presenter { void onFragmentVisible(boolean isVisible); void onRefresh(); - void onStart(View view, OnFragmentIsReadyListener listener); + void attachView(@NonNull View view, OnFragmentIsReadyListener listener); void onSemesterChange(int which); diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesDialogFragment.java index b8da5a96..17ebc6e6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesDialogFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesDialogFragment.java @@ -71,7 +71,7 @@ public class GradesDialogFragment extends DialogFragment { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.grade_dialog, container, false); + View view = inflater.inflate(R.layout.grades_dialog, container, false); ButterKnife.bind(this, view); diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesFragment.java index 5a31736b..ca1b949c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesFragment.java @@ -4,7 +4,6 @@ import android.content.DialogInterface; 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.app.AlertDialog; import android.support.v7.widget.RecyclerView; @@ -14,33 +13,53 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; 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; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; public class GradesFragment extends BaseFragment implements GradesContract.View { + @BindView(R.id.grade_fragment_summary_container) + View summary; + + @BindView(R.id.grade_fragment_details_container) + View details; + @BindView(R.id.grade_fragment_recycler) RecyclerView recyclerView; + @BindView(R.id.grade_fragment_summary_recycler) + RecyclerView summaryRecyclerView; + @BindView(R.id.grade_fragment_no_item_container) View noItemView; @BindView(R.id.grade_fragment_swipe_refresh) SwipeRefreshLayout refreshLayout; + @BindView(R.id.grade_fragment_summary_predicted_average) + TextView predictedAverage; + + @BindView(R.id.grade_fragment_summary_calculated_average) + TextView calculatedAverage; + + @BindView(R.id.grade_fragment_summary_final_average) + TextView finalAverage; + @Inject - FlexibleAdapter adapter; + FlexibleAdapter adapter; + + @Inject + FlexibleAdapter summaryAdapter; @Inject GradesContract.Presenter presenter; @@ -55,14 +74,9 @@ public class GradesFragment extends BaseFragment implements GradesContract.View @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_grades, container, false); + injectViews(view); - FragmentComponent component = getFragmentComponent(); - if (component != null) { - component.inject(this); - setButterKnife(ButterKnife.bind(this, view)); - presenter.onStart(this, (OnFragmentIsReadyListener) getActivity()); - } - + presenter.attachView(this, (OnFragmentIsReadyListener) getActivity()); return view; } @@ -74,43 +88,59 @@ public class GradesFragment extends BaseFragment implements GradesContract.View @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.semester_switch, menu); + menu.clear(); + inflater.inflate(R.menu.grades_action_menu, menu); + super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.action_filter) { - presenter.onSemesterSwitchActive(); - CharSequence[] items = new CharSequence[]{ - getResources().getString(R.string.semester_text, 1), - getResources().getString(R.string.semester_text, 2), - }; - new AlertDialog.Builder(getContext()) - .setTitle(R.string.switch_semester) - .setNegativeButton(R.string.cancel, null) - .setSingleChoiceItems(items, this.currentSemester, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - presenter.onSemesterChange(which); - dialog.cancel(); - } - }).show(); - return true; - } else { - return super.onOptionsItemSelected(item); + switch (item.getItemId()) { + case R.id.action_semester_switch: + presenter.onSemesterSwitchActive(); + CharSequence[] items = new CharSequence[]{ + getResources().getString(R.string.semester_text, 1), + getResources().getString(R.string.semester_text, 2), + }; + new AlertDialog.Builder(getContext()) + .setTitle(R.string.switch_semester) + .setNegativeButton(R.string.cancel, null) + .setSingleChoiceItems(items, this.currentSemester, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + presenter.onSemesterChange(which); + dialog.cancel(); + } + }).show(); + return true; + case R.id.action_summary_switch: + boolean isDetailsVisible = details.getVisibility() == View.VISIBLE; + + item.setTitle(isDetailsVisible ? R.string.action_title_details : R.string.action_title_summary); + details.setVisibility(isDetailsVisible ? View.INVISIBLE : View.VISIBLE); + summary.setVisibility(isDetailsVisible ? View.VISIBLE : View.INVISIBLE); + return true; + default: + return super.onOptionsItemSelected(item); } } @Override - protected void setUpOnViewCreated(View fragmentView) { + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { noItemView.setVisibility(View.GONE); + summary.setVisibility(View.INVISIBLE); + details.setVisibility(View.VISIBLE); adapter.setAutoCollapseOnExpand(true); adapter.setAutoScrollOnExpand(true); adapter.expandItemsAtStartUp(); + summaryAdapter.setDisplayHeadersAtStartUp(true); - recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext())); + recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext())); recyclerView.setAdapter(adapter); + summaryRecyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext())); + summaryRecyclerView.setAdapter(summaryAdapter); + summaryRecyclerView.setNestedScrollingEnabled(false); refreshLayout.setColorSchemeResources(android.R.color.black); refreshLayout.setOnRefreshListener(this); @@ -124,6 +154,13 @@ public class GradesFragment extends BaseFragment implements GradesContract.View } } + @Override + public void setSummaryAverages(String calculatedValue, String predictedValue, String finalValue) { + calculatedAverage.setText(calculatedValue); + predictedAverage.setText(predictedValue); + finalAverage.setText(finalValue); + } + @Override public void setActivityTitle() { setTitle(getString(R.string.grades_text)); @@ -149,31 +186,28 @@ public class GradesFragment extends BaseFragment implements GradesContract.View } @Override - public void updateAdapterList(List headerItems) { + public void updateAdapterList(List headerItems) { adapter.updateDataSet(headerItems); } + @Override + public void updateSummaryAdapterList(List summarySubItems) { + summaryAdapter.updateDataSet(summarySubItems); + } + @Override public void onRefreshSuccessNoGrade() { - onError(R.string.snackbar_no_grades); + showMessage(R.string.snackbar_no_grades); } @Override public void onRefreshSuccess(int number) { - onError(getString(R.string.snackbar_new_grade, number)); - } - - @Override - public void onError(String message) { - if (getActivity() != null) { - Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager), - message, Snackbar.LENGTH_LONG).show(); - } + showMessage(getString(R.string.snackbar_new_grade, number)); } @Override public void onDestroyView() { - presenter.onDestroy(); + presenter.detachView(); super.onDestroyView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradeHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesHeader.java similarity index 77% rename from app/src/main/java/io/github/wulkanowy/ui/main/grades/GradeHeaderItem.java rename to app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesHeader.java index 5d0fd073..c39fcc4d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradeHeaderItem.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesHeader.java @@ -20,14 +20,14 @@ import io.github.wulkanowy.data.db.dao.entities.Subject; import io.github.wulkanowy.utils.AnimationUtils; import io.github.wulkanowy.utils.GradeUtils; -public class GradeHeaderItem - extends AbstractExpandableHeaderItem { +public class GradesHeader + extends AbstractExpandableHeaderItem { private Subject subject; private final boolean isShowSummary; - GradeHeaderItem(Subject subject, boolean isShowSummary) { + GradesHeader(Subject subject, boolean isShowSummary) { this.subject = subject; this.isShowSummary = isShowSummary; } @@ -38,7 +38,7 @@ public class GradeHeaderItem if (o == null || getClass() != o.getClass()) return false; - GradeHeaderItem that = (GradeHeaderItem) o; + GradesHeader that = (GradesHeader) o; return new EqualsBuilder() .append(subject, that.subject) @@ -54,7 +54,7 @@ public class GradeHeaderItem @Override public int getLayoutRes() { - return R.layout.grade_header; + return R.layout.grades_header; } @Override @@ -118,29 +118,41 @@ public class GradeHeaderItem item.getFinalRating())); resetViews(); - toggleSummaryText(); toggleSubjectText(); + toggleSummary(); alertImage.setVisibility(isSubItemsReadAndSaveAlertView(subItems) ? View.INVISIBLE : View.VISIBLE); } + private String getGradesAverageString() { + float average = GradeUtils.calculateWeightedAverage(item.getGradeList()); + + if (average < 0) { + return resources.getString(R.string.info_no_average); + } + + return resources.getString(R.string.info_average_grades, average); + } + @Override public void onClick(View view) { super.onClick(view); toggleSubjectText(); - toggleSummaryText(); + toggleSummary(); } - private void toggleSummaryText() { - if (isSummaryToggleable()) { - if (isExpand()) { - AnimationUtils.slideDown(predictedText); - AnimationUtils.slideDown(finalText); - } else { - AnimationUtils.slideUp(predictedText); - AnimationUtils.slideUp(finalText); - } + private void resetViews() { + subjectName.setMaxLines(1); + setDefaultSummaryVisibility(predictedText, item.getPredictedRating()); + setDefaultSummaryVisibility(finalText, item.getFinalRating()); + } + + private void setDefaultSummaryVisibility(View view, String value) { + if (!"-".equals(value) && isShowSummary) { + view.setVisibility(View.VISIBLE); + } else { + view.setVisibility(View.GONE); } } @@ -152,10 +164,25 @@ public class GradeHeaderItem } } - private void resetViews() { - subjectName.setMaxLines(1); - predictedText.setVisibility(View.GONE); - finalText.setVisibility(View.GONE); + private void toggleSummary() { + toggleSummaryView(predictedText, item.getPredictedRating(), isExpand()); + toggleSummaryView(finalText, item.getFinalRating(), isExpand()); + } + + private boolean isExpand() { + return adapter.isExpanded(getFlexibleAdapterPosition()); + } + + private void toggleSummaryView(View view, String value, boolean expand) { + if ("-".equals(value) || isShowSummary) { + return; + } + + if (expand) { + AnimationUtils.slideDown(view); + } else { + AnimationUtils.slideUp(view); + } } private boolean isSubItemsReadAndSaveAlertView(List subItems) { @@ -168,37 +195,5 @@ public class GradeHeaderItem return isRead; } - - private String getGradesAverageString() { - float average = GradeUtils.calculate(item.getGradeList()); - - if (average < 0) { - return resources.getString(R.string.info_no_average); - } - - return resources.getString(R.string.info_average_grades, average); - } - - private boolean isExpand() { - return adapter.isExpanded(getFlexibleAdapterPosition()); - } - - private boolean isSummaryToggleable() { - boolean isSummaryEmpty = true; - - if (!"-".equals(item.getPredictedRating()) || !"-".equals(item.getFinalRating())) { - isSummaryEmpty = false; - } - - if (isSummaryEmpty) { - return false; - } else if (isShowSummary) { - predictedText.setVisibility(View.VISIBLE); - finalText.setVisibility(View.VISIBLE); - - return false; - } - return true; - } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesModule.java new file mode 100644 index 00000000..25eeef48 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesModule.java @@ -0,0 +1,23 @@ +package io.github.wulkanowy.ui.main.grades; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import eu.davidea.flexibleadapter.FlexibleAdapter; + +@Module +public abstract class GradesModule { + + @Binds + abstract GradesContract.Presenter provideGradesPresenter(GradesPresenter gradesPresenter); + + @Provides + static FlexibleAdapter provideGradesAdapter() { + return new FlexibleAdapter<>(null); + } + + @Provides + static FlexibleAdapter provideGradesSummaryAdapter() { + return new FlexibleAdapter<>(null); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesPresenter.java index 493cc545..0f4d363c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesPresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesPresenter.java @@ -1,5 +1,7 @@ package io.github.wulkanowy.ui.main.grades; +import android.support.annotation.NonNull; + import com.crashlytics.android.answers.Answers; import com.crashlytics.android.answers.CustomEvent; @@ -7,6 +9,7 @@ import org.threeten.bp.LocalDate; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import javax.inject.Inject; @@ -16,6 +19,7 @@ import io.github.wulkanowy.data.db.dao.entities.Subject; import io.github.wulkanowy.ui.base.BasePresenter; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; import io.github.wulkanowy.utils.FabricUtils; +import io.github.wulkanowy.utils.GradeUtils; import io.github.wulkanowy.utils.async.AbstractTask; import io.github.wulkanowy.utils.async.AsyncListeners; @@ -29,20 +33,28 @@ public class GradesPresenter extends BasePresenter private OnFragmentIsReadyListener listener; - private List headerItems = new ArrayList<>(); + private List headerItems = new ArrayList<>(); + + private List summarySubItems = new ArrayList<>(); private boolean isFirstSight = false; private int semesterName; + private float finalAverage; + + private float predictedAverage; + + private float calculatedAverage; + @Inject GradesPresenter(RepositoryContract repository) { super(repository); } @Override - public void onStart(GradesContract.View view, OnFragmentIsReadyListener listener) { - super.onStart(view); + public void attachView(@NonNull GradesContract.View view, OnFragmentIsReadyListener listener) { + super.attachView(view); this.listener = listener; if (getView().isMenuVisible()) { @@ -54,7 +66,6 @@ public class GradesPresenter extends BasePresenter if (!isFirstSight) { isFirstSight = true; - reloadGrades(); } } @@ -74,12 +85,6 @@ public class GradesPresenter extends BasePresenter .putCustomAttribute("Name", semesterName)); } - private void reloadGrades() { - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - } - @Override public void onFragmentVisible(boolean isVisible) { if (isVisible) { @@ -94,7 +99,7 @@ public class GradesPresenter extends BasePresenter refreshTask.setOnRefreshListener(this); refreshTask.execute(); } else { - getView().onNoNetworkError(); + getView().showNoNetworkMessage(); getView().hideRefreshingBar(); } } @@ -125,7 +130,7 @@ public class GradesPresenter extends BasePresenter getView().onRefreshSuccess(numberOfNewGrades); } } else { - getView().onError(getRepository().getResRepo().getErrorLoginMessage(exception)); + getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception)); } getView().hideRefreshingBar(); @@ -138,13 +143,17 @@ public class GradesPresenter extends BasePresenter boolean isShowSummary = getRepository().getSharedRepo().isShowGradesSummary(); headerItems = new ArrayList<>(); + summarySubItems = new ArrayList<>(); for (Subject subject : subjectList) { subject.resetGradeList(); List gradeList = subject.getGradeList(); + GradesSummaryHeader summaryHeader = new GradesSummaryHeader(subject, GradeUtils.calculateWeightedAverage(gradeList)); + summarySubItems.add(new GradesSummarySubItem(summaryHeader, subject)); + if (!gradeList.isEmpty()) { - GradeHeaderItem headerItem = new GradeHeaderItem(subject, isShowSummary); + GradesHeader headerItem = new GradesHeader(subject, isShowSummary); List subItems = new ArrayList<>(); @@ -157,6 +166,10 @@ public class GradesPresenter extends BasePresenter headerItems.add(headerItem); } } + + finalAverage = GradeUtils.calculateSubjectsAverage(subjectList, false); + predictedAverage = GradeUtils.calculateSubjectsAverage(subjectList, true); + calculatedAverage = GradeUtils.calculateDetailedSubjectsAverage(subjectList); } @Override @@ -168,9 +181,35 @@ public class GradesPresenter extends BasePresenter public void onEndLoadingAsync(boolean result, Exception exception) { getView().showNoItem(headerItems.isEmpty()); getView().updateAdapterList(headerItems); + + setSummaryAverages(); + getView().updateSummaryAdapterList(summarySubItems); + listener.onFragmentIsReady(); } + private void setSummaryAverages() { + getView().setSummaryAverages( + getFormattedAverage(calculatedAverage), + getFormattedAverage(predictedAverage), + getFormattedAverage(finalAverage) + ); + } + + private String getFormattedAverage(float average) { + if (-1.0f == average) { + return "-- --"; + } + + return String.format(Locale.FRANCE, "%.2f", average); + } + + private void reloadGrades() { + loadingTask = new AbstractTask(); + loadingTask.setOnFirstLoadingListener(this); + loadingTask.execute(); + } + private void cancelAsyncTasks() { if (refreshTask != null) { refreshTask.cancel(true); @@ -183,9 +222,9 @@ public class GradesPresenter extends BasePresenter } @Override - public void onDestroy() { + public void detachView() { isFirstSight = false; cancelAsyncTasks(); - super.onDestroy(); + super.detachView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSubItem.java index de9c54c6..82b338ae 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSubItem.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSubItem.java @@ -21,7 +21,7 @@ import io.github.wulkanowy.data.db.dao.entities.Grade; import io.github.wulkanowy.utils.GradeUtils; public class GradesSubItem - extends AbstractSectionableItem { + extends AbstractSectionableItem { private Grade grade; @@ -29,7 +29,7 @@ public class GradesSubItem private View subjectAlertImage; - GradesSubItem(GradeHeaderItem header, Grade grade) { + GradesSubItem(GradesHeader header, Grade grade) { super(header); this.grade = grade; } @@ -64,7 +64,7 @@ public class GradesSubItem @Override public int getLayoutRes() { - return R.layout.grade_subitem; + return R.layout.grades_subitem; } @Override diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummaryHeader.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummaryHeader.java new file mode 100644 index 00000000..a06fa0d5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummaryHeader.java @@ -0,0 +1,87 @@ +package io.github.wulkanowy.ui.main.grades; + +import android.view.View; +import android.widget.TextView; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; + +import java.util.List; +import java.util.Locale; + +import butterknife.BindView; +import butterknife.ButterKnife; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.items.AbstractHeaderItem; +import eu.davidea.flexibleadapter.items.IFlexible; +import eu.davidea.viewholders.FlexibleViewHolder; +import io.github.wulkanowy.R; +import io.github.wulkanowy.data.db.dao.entities.Subject; + +class GradesSummaryHeader extends AbstractHeaderItem { + + private Subject subject; + + private String average; + + GradesSummaryHeader(Subject subject, float average) { + this.subject = subject; + this.average = String.format(Locale.FRANCE, "%.2f", average); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + GradesSummaryHeader that = (GradesSummaryHeader) o; + + return new EqualsBuilder() + .append(subject, that.subject) + .append(average, that.average) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(subject) + .append(average) + .toHashCode(); + } + + @Override + public int getLayoutRes() { + return R.layout.grades_summary_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(subject, average); + } + + static class HeaderViewHolder extends FlexibleViewHolder { + + @BindView(R.id.grades_summary_header_name) + TextView name; + + @BindView(R.id.grades_summary_header_average) + TextView average; + + HeaderViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); + } + + void onBind(Subject item, String value) { + name.setText(item.getName()); + average.setText("-1,00".equals(value) ? "" : value); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummarySubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummarySubItem.java new file mode 100644 index 00000000..8ca9e484 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummarySubItem.java @@ -0,0 +1,83 @@ +package io.github.wulkanowy.ui.main.grades; + +import android.view.View; +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.flexibleadapter.items.IFlexible; +import eu.davidea.viewholders.FlexibleViewHolder; +import io.github.wulkanowy.R; +import io.github.wulkanowy.data.db.dao.entities.Subject; + +public class GradesSummarySubItem + extends AbstractSectionableItem { + + private Subject subject; + + public GradesSummarySubItem(GradesSummaryHeader header, Subject subject) { + super(header); + this.subject = subject; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + GradesSummarySubItem that = (GradesSummarySubItem) o; + + return new EqualsBuilder() + .append(subject, that.subject) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(subject) + .toHashCode(); + } + + @Override + public int getLayoutRes() { + return R.layout.grades_summary_subitem; + } + + @Override + public SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new SubItemViewHolder(view, adapter); + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, SubItemViewHolder holder, int position, List payloads) { + holder.onBind(subject); + } + + static class SubItemViewHolder extends FlexibleViewHolder { + + @BindView(R.id.grades_summary_subitem_final_grade) + TextView finalGrade; + + @BindView(R.id.grades_summary_subitem_predicted_grade) + TextView predictedGrade; + + SubItemViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); + } + + void onBind(Subject item) { + predictedGrade.setText(item.getPredictedRating()); + finalGrade.setText(item.getFinalRating()); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/settings/AboutScreen.java b/app/src/main/java/io/github/wulkanowy/ui/main/settings/AboutScreen.java deleted file mode 100644 index fe677de4..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/settings/AboutScreen.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.github.wulkanowy.ui.main.settings; - -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.support.v7.preference.Preference; -import android.widget.Toast; - -import com.google.android.gms.oss.licenses.OssLicensesMenuActivity; - -import io.github.wulkanowy.BuildConfig; -import io.github.wulkanowy.R; -import io.github.wulkanowy.utils.AppConstant; - -public class AboutScreen extends SettingsFragment { - - public static final String SHARED_KEY_ABOUT_VERSION = "about_version"; - - public static final String SHARED_KEY_ABOUT_LICENSES = "about_osl"; - - public static final String SHARED_KEY_ABOUT_REPO = "about_repo"; - - private Preference.OnPreferenceClickListener onProgrammerListener = new Preference.OnPreferenceClickListener() { - private int clicks = 0; - - @Override - public boolean onPreferenceClick(Preference preference) { - Toast.makeText(getActivity(), getVersionToast(clicks++), Toast.LENGTH_SHORT).show(); - return true; - } - - private int getVersionToast(int click) { - if (0 == click) { - return R.string.about_programmer_step1; - } - - if (1 == click) { - return R.string.about_programmer_step2; - } - - if (9 > click) { - return R.string.about_programmer_step3; - } - - return R.string.about_programmer; - } - }; - - public AboutScreen() { - // silence is golden - } - - @Override - public void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - findPreference(SHARED_KEY_ABOUT_VERSION).setSummary(BuildConfig.VERSION_NAME); - findPreference(SHARED_KEY_ABOUT_VERSION).setOnPreferenceClickListener(onProgrammerListener); - findPreference(SHARED_KEY_ABOUT_REPO).setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(AppConstant.REPO_URL))); - findPreference(SHARED_KEY_ABOUT_LICENSES).setIntent(new Intent(getActivity(), OssLicensesMenuActivity.class) - .putExtra("title", getString(R.string.pref_about_osl))); - } - - @Override - public void onCreatePreferences(Bundle bundle, String rootKey) { - setPreferencesFromResource(R.xml.preferences, rootKey); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/settings/SettingsFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/settings/SettingsFragment.java index e08a4902..8bd93024 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/settings/SettingsFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/settings/SettingsFragment.java @@ -1,18 +1,25 @@ package io.github.wulkanowy.ui.main.settings; +import android.content.Intent; import android.content.SharedPreferences; +import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.AppCompatDelegate; +import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceFragmentCompat; -import android.support.v7.preference.PreferenceScreen; +import android.widget.Toast; +import com.google.android.gms.oss.licenses.OssLicensesMenuActivity; + +import io.github.wulkanowy.BuildConfig; import io.github.wulkanowy.R; import io.github.wulkanowy.services.jobs.SyncJob; +import io.github.wulkanowy.ui.main.MainActivity; +import io.github.wulkanowy.utils.AppConstant; public class SettingsFragment extends PreferenceFragmentCompat - implements SharedPreferences.OnSharedPreferenceChangeListener, - PreferenceFragmentCompat.OnPreferenceStartScreenCallback{ + implements SharedPreferences.OnSharedPreferenceChangeListener { public static final String SHARED_KEY_START_TAB = "startup_tab"; @@ -20,6 +27,8 @@ public class SettingsFragment extends PreferenceFragmentCompat public static final String SHARED_KEY_ATTENDANCE_PRESENT = "attendance_present"; + public static final String SHARED_KEY_THEME = "theme"; + public static final String SHARED_KEY_SERVICES_ENABLE = "services_enable"; public static final String SHARED_KEY_NOTIFY_ENABLE = "notify_enable"; @@ -28,6 +37,42 @@ public class SettingsFragment extends PreferenceFragmentCompat public static final String SHARED_KEY_SERVICES_MOBILE_DISABLED = "services_disable_mobile"; + public static final String SHARED_KEY_ABOUT_VERSION = "about_version"; + + public static final String SHARED_KEY_ABOUT_LICENSES = "about_osl"; + + public static final String SHARED_KEY_ABOUT_REPO = "about_repo"; + + private boolean isStarted; + + private boolean isVisible; + + private Preference.OnPreferenceClickListener onProgrammerListener = new Preference.OnPreferenceClickListener() { + private int clicks = 0; + + @Override + public boolean onPreferenceClick(Preference preference) { + Toast.makeText(getActivity(), getVersionToast(clicks++), Toast.LENGTH_SHORT).show(); + return true; + } + + private int getVersionToast(int click) { + if (0 == click) { + return R.string.about_programmer_step1; + } + + if (1 == click) { + return R.string.about_programmer_step2; + } + + if (9 > click) { + return R.string.about_programmer_step3; + } + + return R.string.about_programmer; + } + }; + @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.preferences); @@ -36,6 +81,12 @@ public class SettingsFragment extends PreferenceFragmentCompat @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + findPreference(SHARED_KEY_ABOUT_VERSION).setSummary(BuildConfig.VERSION_NAME); + findPreference(SHARED_KEY_ABOUT_VERSION).setOnPreferenceClickListener(onProgrammerListener); + findPreference(SHARED_KEY_ABOUT_REPO).setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(AppConstant.REPO_URL))); + findPreference(SHARED_KEY_ABOUT_LICENSES).setIntent(new Intent(getActivity(), OssLicensesMenuActivity.class) + .putExtra("title", getString(R.string.pref_about_osl))); } @Override @@ -43,21 +94,6 @@ public class SettingsFragment extends PreferenceFragmentCompat return this; } - @Override - public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat, - PreferenceScreen preferenceScreen) { - FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction(); - AboutScreen fragment = new AboutScreen(); - Bundle args = new Bundle(); - args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, preferenceScreen.getKey()); - fragment.setArguments(args); - ft.add(R.id.main_activity_container, fragment, preferenceScreen.getKey()); - ft.addToBackStack(preferenceScreen.getKey()); - ft.commit(); - - return true; - } - @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals(SHARED_KEY_SERVICES_ENABLE) || key.equals(SHARED_KEY_SERVICES_INTERVAL) @@ -65,6 +101,20 @@ public class SettingsFragment extends PreferenceFragmentCompat launchServices(sharedPreferences.getBoolean(SHARED_KEY_SERVICES_ENABLE, true), sharedPreferences); } + + if (key.equals(SHARED_KEY_THEME)) { + setCurrentTheme(sharedPreferences); + } + } + + private void setCurrentTheme(SharedPreferences sharedPreferences) { + AppCompatDelegate.setDefaultNightMode(Integer.parseInt(sharedPreferences.getString(SHARED_KEY_THEME, "1"))); + getActivity().finish(); + startActivity(MainActivity + .getStartIntent(getContext()) + .putExtra(MainActivity.EXTRA_CARD_ID_KEY, 4) + ); + getActivity().overridePendingTransition(0, 0); } private void launchServices(boolean start, SharedPreferences sharedPref) { @@ -79,11 +129,25 @@ public class SettingsFragment extends PreferenceFragmentCompat } } + private void setTitle() { + getActivity().setTitle(R.string.settings_text); + } + @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (menuVisible) { - getActivity().setTitle(R.string.settings_text); + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + isVisible = isVisibleToUser; + if (isVisible && isStarted) { + setTitle(); + } + } + + @Override + public void onStart() { + super.onStart(); + isStarted = true; + if (isVisible) { + setTitle(); } } @@ -100,4 +164,10 @@ public class SettingsFragment extends PreferenceFragmentCompat getPreferenceScreen().getSharedPreferences() .unregisterOnSharedPreferenceChangeListener(this); } + + @Override + public void onStop() { + super.onStop(); + isStarted = false; + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java index 57eca17e..8344ad76 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java @@ -1,6 +1,7 @@ package io.github.wulkanowy.ui.main.timetable; -import io.github.wulkanowy.di.annotations.PerFragment; +import android.support.annotation.NonNull; + import io.github.wulkanowy.ui.base.BaseContract; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; @@ -21,12 +22,11 @@ public interface TimetableContract { void setThemeForTab(int position); } - @PerFragment interface Presenter extends BaseContract.Presenter { void onFragmentActivated(boolean isVisible); - void onStart(View view, OnFragmentIsReadyListener listener); + void attachView(@NonNull View view, OnFragmentIsReadyListener listener); void setRestoredPosition(int position); } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java index 6c5fd501..db3cc83c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java @@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.main.timetable; 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; @@ -11,14 +10,14 @@ import android.view.View; import android.view.ViewGroup; import javax.inject.Inject; +import javax.inject.Named; 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.base.BasePagerAdapter; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; +import io.github.wulkanowy.ui.main.timetable.tab.TimetableTabFragment; public class TimetableFragment extends BaseFragment implements TimetableContract.View { @@ -30,6 +29,7 @@ public class TimetableFragment extends BaseFragment implements TimetableContract @BindView(R.id.timetable_fragment_tab_layout) TabLayout tabLayout; + @Named("Timetable") @Inject BasePagerAdapter pagerAdapter; @@ -40,16 +40,12 @@ public class TimetableFragment extends BaseFragment implements TimetableContract @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_timetable, container, false); + injectViews(view); - FragmentComponent component = getFragmentComponent(); - if (component != null) { - component.inject(this); - setButterKnife(ButterKnife.bind(this, view)); - presenter.onStart(this, (OnFragmentIsReadyListener) getActivity()); + presenter.attachView(this, (OnFragmentIsReadyListener) getActivity()); - if (savedInstanceState != null) { - presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY)); - } + if (savedInstanceState != null) { + presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY)); } return view; } @@ -91,14 +87,6 @@ public class TimetableFragment extends BaseFragment implements TimetableContract setTitle(getString(R.string.timetable_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 onSaveInstanceState(Bundle outState) { outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem()); @@ -107,7 +95,7 @@ public class TimetableFragment extends BaseFragment implements TimetableContract @Override public void onDestroyView() { - presenter.onDestroy(); + presenter.detachView(); super.onDestroyView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableModule.java new file mode 100644 index 00000000..9ad1ee60 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableModule.java @@ -0,0 +1,32 @@ +package io.github.wulkanowy.ui.main.timetable; + +import javax.inject.Named; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import dagger.android.ContributesAndroidInjector; +import io.github.wulkanowy.di.scopes.PerChildFragment; +import io.github.wulkanowy.di.scopes.PerFragment; +import io.github.wulkanowy.ui.base.BasePagerAdapter; +import io.github.wulkanowy.ui.main.timetable.tab.TimetableTabFragment; +import io.github.wulkanowy.ui.main.timetable.tab.TimetableTabModule; + +@Module +public abstract class TimetableModule { + + @Named("Timetable") + @PerFragment + @Provides + static BasePagerAdapter providePagerAdapter(TimetableFragment fragment) { + return new BasePagerAdapter(fragment.getChildFragmentManager()); + } + + @PerFragment + @Binds + abstract TimetableContract.Presenter provideTimetablePresenter(TimetablePresenter timetablePresenter); + + @PerChildFragment + @ContributesAndroidInjector(modules = TimetableTabModule.class) + abstract TimetableTabFragment bindTimetableTabFragment(); +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java index d9c9c9c6..8c9bfaba 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java @@ -1,5 +1,7 @@ package io.github.wulkanowy.ui.main.timetable; +import android.support.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -31,8 +33,8 @@ public class TimetablePresenter extends BasePresenter } @Override - public void onStart(TimetableContract.View view, OnFragmentIsReadyListener listener) { - super.onStart(view); + public void attachView(@NonNull TimetableContract.View view, OnFragmentIsReadyListener listener) { + super.attachView(view); this.listener = listener; if (getView().isMenuVisible()) { @@ -64,7 +66,7 @@ public class TimetablePresenter extends BasePresenter } @Override - public void onDoInBackgroundLoading() throws Exception { + public void onDoInBackgroundLoading() { for (String date : dates) { getView().setTabDataToAdapter(date); } @@ -91,13 +93,13 @@ public class TimetablePresenter extends BasePresenter } @Override - public void onDestroy() { + public void detachView() { isFirstSight = false; if (loadingTask != null) { loadingTask.cancel(true); loadingTask = null; } - super.onDestroy(); + super.detachView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableHeader.java similarity index 84% rename from app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java rename to app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableHeader.java index b98636b1..cb877387 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableHeader.java @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.main.timetable; +package io.github.wulkanowy.ui.main.timetable.tab; import android.content.Context; import android.content.res.TypedArray; @@ -14,7 +14,6 @@ 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; @@ -23,13 +22,14 @@ import eu.davidea.flexibleadapter.items.IFlexible; import eu.davidea.viewholders.ExpandableViewHolder; import io.github.wulkanowy.R; import io.github.wulkanowy.data.db.dao.entities.Day; +import io.github.wulkanowy.utils.CommonUtils; -public class TimetableHeaderItem - extends AbstractExpandableHeaderItem { +public class TimetableHeader + extends AbstractExpandableHeaderItem { private Day day; - TimetableHeaderItem(Day day) { + TimetableHeader(Day day) { this.day = day; } @@ -39,7 +39,7 @@ public class TimetableHeaderItem if (o == null || getClass() != o.getClass()) return false; - TimetableHeaderItem that = (TimetableHeaderItem) o; + TimetableHeader that = (TimetableHeader) o; return new EqualsBuilder() .append(day, that.day) @@ -83,15 +83,6 @@ public class TimetableHeaderItem @BindView(R.id.timetable_header_free_name) TextView freeName; - @BindColor(R.color.secondary_text) - int secondaryColor; - - @BindColor(R.color.free_day) - int backgroundFreeDay; - - @BindColor(android.R.color.black) - int black; - private Context context; HeaderViewHolder(View view, FlexibleAdapter adapter) { @@ -112,13 +103,13 @@ public class TimetableHeaderItem private void setInactiveHeader(boolean inactive) { ((FrameLayout) getContentView()).setForeground(inactive ? null : getSelectableDrawable()); - dayName.setTextColor(inactive ? secondaryColor : black); + dayName.setTextColor(CommonUtils.getThemeAttrColor(context, + inactive ? android.R.attr.textColorSecondary : android.R.attr.textColorPrimary)); if (inactive) { - getContentView().setBackgroundColor(backgroundFreeDay); + getContentView().setBackgroundColor(CommonUtils.getThemeAttrColor(context, R.attr.colorControlHighlight)); } else { - getContentView().setBackgroundDrawable(context.getResources() - .getDrawable(R.drawable.ic_border)); + getContentView().setBackgroundDrawable(context.getResources().getDrawable(R.drawable.ic_border)); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableSubItem.java similarity index 95% rename from app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java rename to app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableSubItem.java index a3e84dfa..2bf47b33 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableSubItem.java @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.main.timetable; +package io.github.wulkanowy.ui.main.timetable.tab; import android.content.Context; import android.graphics.Paint; @@ -21,14 +21,15 @@ import eu.davidea.flexibleadapter.items.IFlexible; import eu.davidea.viewholders.FlexibleViewHolder; import io.github.wulkanowy.R; import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; +import io.github.wulkanowy.ui.main.timetable.TimetableDialogFragment; public class TimetableSubItem - extends AbstractSectionableItem { + extends AbstractSectionableItem { private TimetableLesson lesson; - TimetableSubItem(TimetableHeaderItem header, TimetableLesson lesson) { + TimetableSubItem(TimetableHeader header, TimetableLesson lesson) { super(header); this.lesson = lesson; } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabContract.java similarity index 78% rename from app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabContract.java rename to app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabContract.java index 0d6bdf2a..0814ff3c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabContract.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabContract.java @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.main.timetable; +package io.github.wulkanowy.ui.main.timetable.tab; import java.util.List; @@ -8,7 +8,9 @@ public interface TimetableTabContract { interface View extends BaseContract.View { - void updateAdapterList(List headerItems); + void updateAdapterList(List headerItems); + + void expandItem(int item); void onRefreshSuccess(); diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabFragment.java similarity index 74% rename from app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabFragment.java rename to app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabFragment.java index 53d9a67b..87db5195 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabFragment.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabFragment.java @@ -1,9 +1,8 @@ -package io.github.wulkanowy.ui.main.timetable; +package io.github.wulkanowy.ui.main.timetable.tab; 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; @@ -16,11 +15,9 @@ 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 TimetableTabFragment extends BaseFragment implements TimetableTabContract.View, @@ -47,7 +44,7 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo TimetableTabContract.Presenter presenter; @Inject - FlexibleAdapter adapter; + FlexibleAdapter adapter; private boolean isFragmentVisible = false; @@ -65,40 +62,40 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_timetable_tab, container, false); + injectViews(view); - 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); - presenter.onFragmentActivated(isFragmentVisible); + if (getArguments() != null) { + presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY)); } + presenter.attachView(this); + presenter.onFragmentActivated(isFragmentVisible); return view; } @Override - protected void setUpOnViewCreated(View fragmentView) { + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { adapter.setAutoCollapseOnExpand(true); adapter.setAutoScrollOnExpand(true); adapter.expandItemsAtStartUp(); - recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext())); + recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext())); recyclerView.setAdapter(adapter); refreshLayout.setColorSchemeResources(android.R.color.black); refreshLayout.setOnRefreshListener(this); - } @Override - public void updateAdapterList(List headerItems) { + public void updateAdapterList(List headerItems) { adapter.updateDataSet(headerItems); } + @Override + public void expandItem(int position) { + adapter.expand(adapter.getItem(position), true); + recyclerView.scrollToPosition(position); + } + @Override public void setMenuVisibility(boolean menuVisible) { super.setMenuVisibility(menuVisible); @@ -120,7 +117,7 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo @Override public void onRefreshSuccess() { - onError(R.string.sync_completed); + showMessage(R.string.sync_completed); } @Override @@ -138,17 +135,9 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); } - @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() { - presenter.onDestroy(); + presenter.detachView(); super.onDestroyView(); } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabModule.java new file mode 100644 index 00000000..1afb055f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabModule.java @@ -0,0 +1,21 @@ +package io.github.wulkanowy.ui.main.timetable.tab; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import io.github.wulkanowy.di.scopes.PerChildFragment; + +@Module +public abstract class TimetableTabModule { + + @PerChildFragment + @Binds + abstract TimetableTabContract.Presenter provideTimetableTabPresneter(TimetableTabPresenter timetableTabPresenter); + + @PerChildFragment + @Provides + static FlexibleAdapter provideTimetableAdapter() { + return new FlexibleAdapter<>(null); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabPresenter.java similarity index 80% rename from app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java rename to app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabPresenter.java index ef41c4c7..42f2424d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabPresenter.java @@ -1,4 +1,8 @@ -package io.github.wulkanowy.ui.main.timetable; +package io.github.wulkanowy.ui.main.timetable.tab; + +import android.support.annotation.NonNull; + +import org.threeten.bp.LocalDate; import java.util.ArrayList; import java.util.List; @@ -10,7 +14,9 @@ import io.github.wulkanowy.data.db.dao.entities.Day; 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.AppConstant; import io.github.wulkanowy.utils.FabricUtils; +import io.github.wulkanowy.utils.TimeUtils; import io.github.wulkanowy.utils.async.AbstractTask; import io.github.wulkanowy.utils.async.AsyncListeners; @@ -22,7 +28,7 @@ public class TimetableTabPresenter extends BasePresenter headerItems = new ArrayList<>(); + private List headerItems = new ArrayList<>(); private String date; @@ -36,8 +42,8 @@ public class TimetableTabPresenter extends BasePresenter { } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashModule.java b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashModule.java new file mode 100644 index 00000000..838b411d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashModule.java @@ -0,0 +1,13 @@ +package io.github.wulkanowy.ui.splash; + +import dagger.Binds; +import dagger.Module; +import io.github.wulkanowy.di.scopes.PerActivity; + +@Module +public abstract class SplashModule { + + @PerActivity + @Binds + abstract SplashContract.Presenter provideSplashPresenter(SplashPresenter splashPresenter); +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.java index 8d20226f..69423348 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.java +++ b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.java @@ -16,8 +16,9 @@ public class SplashPresenter extends BasePresenter } @Override - public void onStart(@NonNull SplashContract.View activity) { - super.onStart(activity); + public void attachView(@NonNull SplashContract.View view) { + super.attachView(view); + getView().setCurrentThemeMode(getRepository().getSharedRepo().getCurrentTheme()); getView().cancelNotifications(); if (getRepository().getSharedRepo().isUserLoggedIn()) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetFactory.java b/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetFactory.java index 78e80580..395e1c07 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetFactory.java +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetFactory.java @@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.widgets; import android.content.Context; import android.content.Intent; +import android.graphics.Paint; import android.view.View; import android.widget.AdapterView; import android.widget.RemoteViews; @@ -12,10 +13,7 @@ import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.List; -import javax.inject.Inject; - import io.github.wulkanowy.R; -import io.github.wulkanowy.WulkanowyApp; import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; import io.github.wulkanowy.data.db.dao.entities.Week; @@ -23,22 +21,17 @@ import io.github.wulkanowy.utils.TimeUtils; public class TimetableWidgetFactory implements RemoteViewsService.RemoteViewsFactory { - private Context context; + private final Context context; private List lessonList = new ArrayList<>(); - @Inject - RepositoryContract repository; + private final RepositoryContract repository; - public TimetableWidgetFactory(Context context) { + public TimetableWidgetFactory(Context context, RepositoryContract repository) { this.context = context; + this.repository = repository; } - private void inject() { - if (repository == null) { - ((WulkanowyApp) context).getApplicationComponent().inject(this); - } - } @Override public void onCreate() { @@ -47,7 +40,6 @@ public class TimetableWidgetFactory implements RemoteViewsService.RemoteViewsFac @Override public void onDataSetChanged() { - inject(); lessonList = new ArrayList<>(); if (repository.getSharedRepo().isUserLoggedIn()) { @@ -84,13 +76,21 @@ public class TimetableWidgetFactory implements RemoteViewsService.RemoteViewsFac views.setTextViewText(R.id.timetable_widget_item_room, getRoomText(position)); if (!getDescriptionText(position).isEmpty()) { + views.setViewVisibility(R.id.timetable_widget_item_description, View.VISIBLE); views.setTextViewText(R.id.timetable_widget_item_description, getDescriptionText(position)); } else { views.setViewVisibility(R.id.timetable_widget_item_description, View.GONE); } - views.setOnClickFillInIntent(R.id.timetable_widget_item_container, new Intent()); + if (lessonList.get(position).getMovedOrCanceled()) { + views.setInt(R.id.timetable_widget_item_subject, "setPaintFlags", + Paint.STRIKE_THRU_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG); + } else { + views.setInt(R.id.timetable_widget_item_subject, "setPaintFlags", + Paint.ANTI_ALIAS_FLAG); + } + views.setOnClickFillInIntent(R.id.timetable_widget_item_container, new Intent()); return views; } diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetProvider.java b/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetProvider.java index d68c7e9a..84f618a6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetProvider.java +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetProvider.java @@ -11,8 +11,8 @@ import android.widget.RemoteViews; import javax.inject.Inject; +import dagger.android.AndroidInjection; import io.github.wulkanowy.R; -import io.github.wulkanowy.WulkanowyApp; import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.services.widgets.TimetableWidgetServices; import io.github.wulkanowy.ui.main.MainActivity; @@ -26,7 +26,6 @@ public class TimetableWidgetProvider extends AppWidgetProvider { RepositoryContract repository; @Override - public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { inject(context); @@ -106,7 +105,7 @@ public class TimetableWidgetProvider extends AppWidgetProvider { private void inject(Context context) { if (repository == null) { - ((WulkanowyApp) context.getApplicationContext()).getApplicationComponent().inject(this); + AndroidInjection.inject(this, context); } } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java b/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java index 8ec73faf..9c7b35fb 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java +++ b/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java @@ -1,7 +1,11 @@ package io.github.wulkanowy.utils; import android.app.Activity; +import android.content.Context; +import android.content.res.TypedArray; import android.net.Uri; +import android.support.annotation.AttrRes; +import android.support.annotation.ColorInt; import android.support.customtabs.CustomTabsIntent; import io.github.wulkanowy.R; @@ -37,4 +41,14 @@ public final class CommonUtils { return R.string.noColor_text; } } + + @ColorInt + public static int getThemeAttrColor(Context context, @AttrRes int colorAttr) { + final TypedArray array = context.obtainStyledAttributes(null, new int[]{colorAttr}); + try { + return array.getColor(0, 0); + } finally { + array.recycle(); + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java index 3f733ad6..1747fc26 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java +++ b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java @@ -21,10 +21,10 @@ public final class DataObjectConverter { throw new IllegalStateException("Utility class"); } - public static List studentsToStudentEntities(List students, Long symbolId) { + public static List studentsToStudentEntities(List students, Long symbolId) { List studentList = new ArrayList<>(); - for (io.github.wulkanowy.api.Student student : students) { + for (io.github.wulkanowy.api.generic.Student student : students) { studentList.add(new Student() .setName(student.getName()) .setCurrent(student.isCurrent()) @@ -36,10 +36,10 @@ public final class DataObjectConverter { return studentList; } - public static List diariesToDiaryEntities(List diaryList, Long studentId) { + public static List diariesToDiaryEntities(List diaryList, Long studentId) { List diaryEntityList = new ArrayList<>(); - for (io.github.wulkanowy.api.Diary diary : diaryList) { + for (io.github.wulkanowy.api.generic.Diary diary : diaryList) { diaryEntityList.add(new Diary() .setStudentId(studentId) .setValue(diary.getId()) @@ -50,10 +50,10 @@ public final class DataObjectConverter { return diaryEntityList; } - public static List semestersToSemesterEntities(List semesters, long diaryId) { + public static List semestersToSemesterEntities(List semesters, long diaryId) { List semesterList = new ArrayList<>(); - for (io.github.wulkanowy.api.Semester semester : semesters) { + for (io.github.wulkanowy.api.generic.Semester semester : semesters) { semesterList.add(new Semester() .setDiaryId(diaryId) .setName(semester.getName()) diff --git a/app/src/main/java/io/github/wulkanowy/utils/FabricUtils.java b/app/src/main/java/io/github/wulkanowy/utils/FabricUtils.java index 38811534..69897caa 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/FabricUtils.java +++ b/app/src/main/java/io/github/wulkanowy/utils/FabricUtils.java @@ -2,6 +2,7 @@ package io.github.wulkanowy.utils; import com.crashlytics.android.answers.Answers; import com.crashlytics.android.answers.CustomEvent; +import com.crashlytics.android.answers.LoginEvent; import com.crashlytics.android.answers.SignUpEvent; public final class FabricUtils { @@ -10,17 +11,26 @@ public final class FabricUtils { throw new IllegalStateException("Utility class"); } - public static void logRegister(boolean result, String symbol) { + public static void logLogin(String method, boolean result) { + Answers.getInstance().logLogin(new LoginEvent() + .putMethod(method) + .putSuccess(result) + ); + } + + public static void logRegister(boolean result, String symbol, String message) { Answers.getInstance().logSignUp(new SignUpEvent() .putMethod("Login activity") .putSuccess(result) - .putCustomAttribute("symbol", symbol)); + .putCustomAttribute("symbol", symbol) + .putCustomAttribute("message", message) + ); } public static void logRefresh(String name, boolean result, String date) { Answers.getInstance().logCustom( new CustomEvent(name + " refresh") - .putCustomAttribute("Success", result ? 1 : 0) + .putCustomAttribute("Success", result ? "true" : "false") .putCustomAttribute("Date", date) ); } diff --git a/app/src/main/java/io/github/wulkanowy/utils/GradeUtils.java b/app/src/main/java/io/github/wulkanowy/utils/GradeUtils.java index 32bc5cd0..29111457 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/GradeUtils.java +++ b/app/src/main/java/io/github/wulkanowy/utils/GradeUtils.java @@ -6,6 +6,7 @@ import java.util.regex.Pattern; import io.github.wulkanowy.R; import io.github.wulkanowy.data.db.dao.entities.Grade; +import io.github.wulkanowy.data.db.dao.entities.Subject; public final class GradeUtils { @@ -16,53 +17,33 @@ public final class GradeUtils { throw new IllegalStateException("Utility class"); } - public static float calculate(List gradeList) { + public static float calculateWeightedAverage(List gradeList) { float counter = 0f; float denominator = 0f; for (Grade grade : gradeList) { - int integerWeight = getIntegerForWeightOfGrade(grade.getWeight()); - float floatValue = getMathematicalValueOfGrade(grade.getValue()); + int weight = getWeightValue(grade.getWeight()); + float value = getWeightedGradeValue(grade.getValue()); - if (floatValue != -1f) { - counter += floatValue * integerWeight; - denominator += integerWeight; + if (value != -1.0f) { + counter += value * weight; + denominator += weight; } } if (counter == 0f) { - return -1f; - } else { - return counter / denominator; + return -1.0f; } + return counter / denominator; } - private static float getMathematicalValueOfGrade(String valueOfGrade) { - if (valueOfGrade.matches("[-|+|=]{0,2}[0-6]") - || valueOfGrade.matches("[0-6][-|+|=]{0,2}")) { - if (valueOfGrade.matches("[-][0-6]") - || valueOfGrade.matches("[0-6][-]")) { - String replacedValue = valueOfGrade.replaceAll("[-]", ""); - return Float.valueOf(replacedValue) - 0.33f; - } else if (valueOfGrade.matches("[+][0-6]") - || valueOfGrade.matches("[0-6][+]")) { - String replacedValue = valueOfGrade.replaceAll("[+]", ""); - return Float.valueOf((replacedValue)) + 0.33f; - } else if (valueOfGrade.matches("[-|=]{1,2}[0-6]") - || valueOfGrade.matches("[0-6][-|=]{1,2}")) { - String replacedValue = valueOfGrade.replaceAll("[-|=]{1,2}", ""); - return Float.valueOf((replacedValue)) - 0.5f; - } else { - return Float.valueOf(valueOfGrade); - } - } else { - return -1; - } + public static float calculateSubjectsAverage(List subjectList, boolean usePredicted) { + return calculateSubjectsAverage(subjectList, usePredicted, false); } - private static int getIntegerForWeightOfGrade(String weightOfGrade) { - return Integer.valueOf(weightOfGrade.substring(0, weightOfGrade.length() - 3)); + public static float calculateDetailedSubjectsAverage(List subjectList) { + return calculateSubjectsAverage(subjectList, false, true); } public static int getValueColor(String value) { @@ -93,4 +74,80 @@ public final class GradeUtils { return R.color.default_grade; } } + + private static float calculateSubjectsAverage(List subjectList, boolean usePredicted, boolean useSubjectsAverages) { + float counter = 0f; + float denominator = 0f; + + for (Subject subject : subjectList) { + float value; + + if (useSubjectsAverages) { + value = calculateWeightedAverage(subject.getGradeList()); + } else { + value = getGradeValue(usePredicted ? subject.getPredictedRating() : subject.getFinalRating()); + } + + if (value != -1.0f) { + counter += Math.round(value); + denominator++; + } + } + + if (counter == 0) { + return -1.0f; + } + + return counter / denominator; + } + + private static float getGradeValue(String grade) { + if (validGradePattern.matcher(grade).matches()) { + return getWeightedGradeValue(grade); + } + + return getVerbalGradeValue(grade); + } + + private static float getVerbalGradeValue(String grade) { + switch (grade) { + case "celujący": + return 6f; + case "bardzo dobry": + return 5f; + case "dobry": + return 4f; + case "dostateczny": + return 3f; + case "dopuszczający": + return 2f; + case "niedostateczny": + return 1f; + default: + return -1f; + } + } + + private static float getWeightedGradeValue(String value) { + if (validGradePattern.matcher(value).matches()) { + if (value.matches("[-][0-6]") || value.matches("[0-6][-]")) { + String replacedValue = value.replaceAll("[-]", ""); + return Float.valueOf(replacedValue) - 0.33f; + } else if (value.matches("[+][0-6]") || value.matches("[0-6][+]")) { + String replacedValue = value.replaceAll("[+]", ""); + return Float.valueOf((replacedValue)) + 0.33f; + } else if (value.matches("[-|=]{1,2}[0-6]") || value.matches("[0-6][-|=]{1,2}")) { + String replacedValue = value.replaceAll("[-|=]{1,2}", ""); + return Float.valueOf((replacedValue)) - 0.5f; + } else { + return Float.valueOf(value); + } + } else { + return -1; + } + } + + private static int getWeightValue(String weightOfGrade) { + return Integer.valueOf(weightOfGrade.substring(0, weightOfGrade.length() - 3)); + } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/LogUtils.java b/app/src/main/java/io/github/wulkanowy/utils/LogUtils.java deleted file mode 100644 index f59bbf64..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/LogUtils.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.utils; - -import android.util.Log; - -public final class LogUtils { - - private LogUtils() { - throw new IllegalStateException("Utility class"); - } - - public static void debug(String message) { - Log.d(AppConstant.APP_NAME, message); - } - - public static void error(String message, Throwable throwable) { - Log.e(AppConstant.APP_NAME, message, throwable); - } - - public static void error(String message) { - Log.e(AppConstant.APP_NAME, message); - } - - public static void info(String message) { - Log.i(AppConstant.APP_NAME, message); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.java b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.java new file mode 100644 index 00000000..6e6c701b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.java @@ -0,0 +1,47 @@ +package io.github.wulkanowy.utils; + +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import com.crashlytics.android.Crashlytics; + +import timber.log.Timber; + +public final class LoggerUtils { + + public static class CrashlyticsTree extends Timber.Tree { + + @Override + protected void log(int priority, @Nullable String tag, @Nullable String message, @Nullable Throwable t) { + Crashlytics.setInt("priority", priority); + Crashlytics.setString("tag", tag); + + if (t == null) { + Crashlytics.log(message); + } else { + Crashlytics.setString("message", message); + Crashlytics.logException(t); + } + } + } + + public static class DebugLogTree extends Timber.DebugTree { + + @Override + protected void log(int priority, String tag, @NonNull String message, Throwable t) { + if ("HUAWEI".equals(Build.MANUFACTURER) || "samsung".equals(Build.MANUFACTURER)) { + if (priority == Log.VERBOSE || priority == Log.DEBUG || priority == Log.INFO) { + priority = Log.ERROR; + } + } + super.log(priority, AppConstant.APP_NAME, message, t); + } + + @Override + protected String createStackElementTag(@NonNull StackTraceElement element) { + return super.createStackElementTag(element) + " - " + element.getLineNumber(); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.java b/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.java index de5d136f..a305bc6d 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.java +++ b/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.java @@ -4,48 +4,19 @@ import org.threeten.bp.DayOfWeek; import org.threeten.bp.LocalDate; import org.threeten.bp.format.DateTimeFormatter; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; import java.util.List; -import java.util.Locale; -import java.util.TimeZone; public final class TimeUtils { - private static final long TICKS_AT_EPOCH = 621355968000000000L; - - private static final long TICKS_PER_MILLISECOND = 10000; - private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(AppConstant.DATE_PATTERN); private TimeUtils() { throw new IllegalStateException("Utility class"); } - public static long getNetTicks(Date date) { - Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - - return (calendar.getTimeInMillis() * TICKS_PER_MILLISECOND) + TICKS_AT_EPOCH; - } - - public static long getNetTicks(String dateString) throws ParseException { - return getNetTicks(dateString, AppConstant.DATE_PATTERN); - } - - public static long getNetTicks(String dateString, String dateFormat) throws ParseException { - SimpleDateFormat format = new SimpleDateFormat(dateFormat, Locale.ROOT); - format.setTimeZone(TimeZone.getTimeZone("UTC")); - Date dateObject = format.parse(dateString); - - return getNetTicks(dateObject); - } - - public static Date getDate(long netTicks) { - return new Date((netTicks - TICKS_AT_EPOCH) / TICKS_PER_MILLISECOND); + public static LocalDate getParsedDate(String dateString, String dateFormat) { + return LocalDate.parse(dateString, DateTimeFormatter.ofPattern(dateFormat)); } public static List getMondaysFromCurrentSchoolYear() { @@ -102,4 +73,9 @@ public final class TimeUtils { LocalDate current = LocalDate.now(); return nextDay ? current.plusDays(1).format(formatter) : current.format(formatter); } + + public static boolean isDateInWeek(LocalDate firstWeekDay, LocalDate date) { + return date.isAfter(firstWeekDay.minusDays(1)) && date.isBefore(firstWeekDay.plusDays(5)); + + } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java b/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java index 5106d641..44721416 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java +++ b/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java @@ -2,7 +2,7 @@ package io.github.wulkanowy.utils.async; import android.os.AsyncTask; -import io.github.wulkanowy.utils.LogUtils; +import timber.log.Timber; public class AbstractTask extends AsyncTask { @@ -28,7 +28,7 @@ public class AbstractTask extends AsyncTask { } else if (onRefreshListener != null) { onRefreshListener.onDoInBackgroundRefresh(); } else { - LogUtils.error("AbstractTask does not have a listener assigned"); + Timber.e("AbstractTask does not have a listener assigned"); } return true; } catch (Exception e) { @@ -45,7 +45,7 @@ public class AbstractTask extends AsyncTask { } else if (onRefreshListener != null) { onRefreshListener.onCanceledRefreshAsync(); } else { - LogUtils.error("AbstractTask does not have a listener assigned"); + Timber.e("AbstractTask does not have a listener assigned"); } } @@ -57,7 +57,7 @@ public class AbstractTask extends AsyncTask { } else if (onRefreshListener != null) { onRefreshListener.onEndRefreshAsync(result, exception); } else { - LogUtils.error("AbstractTask does not have a listener assigned"); + Timber.e("AbstractTask does not have a listener assigned"); } } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java index dc0c409a..8e425a89 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java +++ b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java @@ -26,7 +26,7 @@ import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.security.auth.x500.X500Principal; -import io.github.wulkanowy.utils.LogUtils; +import timber.log.Timber; public final class Scrambler { @@ -111,7 +111,7 @@ public final class Scrambler { throw new CryptoException("GenerateNewKey - String is empty"); } - LogUtils.debug("Key pair are create"); + Timber.d("Key pair are create"); } diff --git a/app/src/main/play/pl-PL/whatsnew b/app/src/main/play/pl-PL/whatsnew index dd43c504..50d65699 100644 --- a/app/src/main/play/pl-PL/whatsnew +++ b/app/src/main/play/pl-PL/whatsnew @@ -1,5 +1,8 @@ -Wersja 0.4.4: -- naprawiono błędy w synchronizacji planu lekcji -- naprawiono błędy podczas pierwszego logowania -- naprawiono błąd podczas zmiany semestru -- kolejny raz poprawiono synchronizację w tle +Wersja 0.5.0: +- dodano podsumowanie ocen z przedmiotów +- dodano ciemny motyw (wczesna wersja) +- otwieranie aktualnego dnia w planie lekcji +- ukrywanie górnego paska przy przewijaniu +- naprawa wyświetlania zastępstw w widgecie +- poprawa wyświetlania końcowych ocen +- poprawa pierwsze logowania diff --git a/app/src/main/res/drawable-night/ic_border.xml b/app/src/main/res/drawable-night/ic_border.xml new file mode 100644 index 00000000..ca44eb29 --- /dev/null +++ b/app/src/main/res/drawable-night/ic_border.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/res/drawable/ic_action_menu_semester.xml b/app/src/main/res/drawable/ic_action_menu_semester.xml new file mode 100644 index 00000000..b7acb9df --- /dev/null +++ b/app/src/main/res/drawable/ic_action_menu_semester.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_action_menu_summary.xml b/app/src/main/res/drawable/ic_action_menu_summary.xml new file mode 100644 index 00000000..1b2a93b8 --- /dev/null +++ b/app/src/main/res/drawable/ic_action_menu_summary.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/ic_border.xml b/app/src/main/res/drawable/ic_border.xml index dc33b8aa..f46b3ad7 100644 --- a/app/src/main/res/drawable/ic_border.xml +++ b/app/src/main/res/drawable/ic_border.xml @@ -6,4 +6,4 @@ android:centerColor="@android:color/transparent" android:centerX="0.01" android:startColor="#60606060" /> - \ No newline at end of file + diff --git a/app/src/main/res/drawable/ic_exclamation_24dp.xml b/app/src/main/res/drawable/ic_exclamation_24dp.xml index 4d3c91cf..14950458 100644 --- a/app/src/main/res/drawable/ic_exclamation_24dp.xml +++ b/app/src/main/res/drawable/ic_exclamation_24dp.xml @@ -1,4 +1,4 @@ - + - - diff --git a/app/src/main/res/drawable/ic_menu_attendance_24dp.xml b/app/src/main/res/drawable/ic_menu_attendance_24dp.xml index fdef7322..c0df16d6 100644 --- a/app/src/main/res/drawable/ic_menu_attendance_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_attendance_24dp.xml @@ -5,8 +5,8 @@ android:viewportHeight="24" android:viewportWidth="24"> - \ No newline at end of file + diff --git a/app/src/main/res/drawable/ic_menu_exams_24dp.xml b/app/src/main/res/drawable/ic_menu_exams_24dp.xml index 66294598..13179b7c 100644 --- a/app/src/main/res/drawable/ic_menu_exams_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_exams_24dp.xml @@ -4,7 +4,7 @@ android:viewportHeight="24" android:viewportWidth="24"> diff --git a/app/src/main/res/drawable/ic_menu_grade_26dp.xml b/app/src/main/res/drawable/ic_menu_grade_26dp.xml index 61506dad..3866cd3c 100644 --- a/app/src/main/res/drawable/ic_menu_grade_26dp.xml +++ b/app/src/main/res/drawable/ic_menu_grade_26dp.xml @@ -4,8 +4,8 @@ android:viewportHeight="26" android:viewportWidth="26"> - \ No newline at end of file + diff --git a/app/src/main/res/drawable/ic_menu_other_24dp.xml b/app/src/main/res/drawable/ic_menu_other_24dp.xml index 9fed586b..a0774724 100644 --- a/app/src/main/res/drawable/ic_menu_other_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_other_24dp.xml @@ -5,7 +5,7 @@ android:viewportHeight="24" android:viewportWidth="24"> - \ No newline at end of file + diff --git a/app/src/main/res/drawable/ic_menu_timetable_24dp.xml b/app/src/main/res/drawable/ic_menu_timetable_24dp.xml index a689dc06..63880977 100644 --- a/app/src/main/res/drawable/ic_menu_timetable_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_timetable_24dp.xml @@ -5,7 +5,7 @@ android:viewportHeight="24" android:viewportWidth="24"> - \ No newline at end of file + diff --git a/app/src/main/res/drawable/ic_wrench_24dp.xml b/app/src/main/res/drawable/ic_wrench_24dp.xml deleted file mode 100644 index 8f3a86fa..00000000 --- a/app/src/main/res/drawable/ic_wrench_24dp.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f9201ad4..3f8473d8 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,37 +1,28 @@ - - - - - - + android:baselineAligned="false" + android:weightSum="1"> + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1"> + + + + + + android:layout_marginBottom="@dimen/bottom_navigation_height" + app:layout_behavior="@string/appbar_scrolling_view_behavior" /> - - + \ No newline at end of file diff --git a/app/src/main/res/layout/attendance_dialog.xml b/app/src/main/res/layout/attendance_dialog.xml index 916c993c..4b0f1ff9 100644 --- a/app/src/main/res/layout/attendance_dialog.xml +++ b/app/src/main/res/layout/attendance_dialog.xml @@ -144,6 +144,7 @@ android:background="?attr/selectableItemBackground" android:focusable="true" android:text="@string/generic_dialog_close" + android:textColor="?android:attr/android:textColorSecondary" android:textAllCaps="true" android:textSize="15sp" /> diff --git a/app/src/main/res/layout/attendance_header.xml b/app/src/main/res/layout/attendance_header.xml index 61dac631..350b2452 100644 --- a/app/src/main/res/layout/attendance_header.xml +++ b/app/src/main/res/layout/attendance_header.xml @@ -39,7 +39,7 @@ android:layout_marginTop="5dp" android:maxLines="1" android:text="@string/app_name" - android:textColor="@color/secondary_text" + android:textColor="?android:attr/android:textColorSecondary" android:textSize="14sp" /> - \ No newline at end of file + android:textColor="?attr/colorPrimary" + android:textSize="14sp" /> diff --git a/app/src/main/res/layout/exams_dialog.xml b/app/src/main/res/layout/exams_dialog.xml index 47fd7bb7..72d4b8dd 100644 --- a/app/src/main/res/layout/exams_dialog.xml +++ b/app/src/main/res/layout/exams_dialog.xml @@ -169,6 +169,7 @@ android:background="?attr/selectableItemBackground" android:focusable="true" android:text="@string/generic_dialog_close" + android:textColor="?android:attr/android:textColorSecondary" android:textAllCaps="true" android:textSize="15sp" /> diff --git a/app/src/main/res/layout/exams_header.xml b/app/src/main/res/layout/exams_header.xml index 3895ae2e..7f7bf65f 100644 --- a/app/src/main/res/layout/exams_header.xml +++ b/app/src/main/res/layout/exams_header.xml @@ -2,7 +2,7 @@ - \ No newline at end of file + diff --git a/app/src/main/res/layout/exams_subitem.xml b/app/src/main/res/layout/exams_subitem.xml index 52386de4..3f50f07c 100644 --- a/app/src/main/res/layout/exams_subitem.xml +++ b/app/src/main/res/layout/exams_subitem.xml @@ -53,4 +53,4 @@ android:text="@string/app_name" android:textSize="13sp" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/fragment_attendance_tab.xml b/app/src/main/res/layout/fragment_attendance_tab.xml index f98e7280..4dc450b5 100644 --- a/app/src/main/res/layout/fragment_attendance_tab.xml +++ b/app/src/main/res/layout/fragment_attendance_tab.xml @@ -1,6 +1,6 @@ - @@ -43,8 +43,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="46dp" - android:text="@string/attendance_no_entries" android:gravity="center" + android:text="@string/attendance_no_entries" android:textSize="20sp" /> diff --git a/app/src/main/res/layout/fragment_exams_tab.xml b/app/src/main/res/layout/fragment_exams_tab.xml index e446b2fb..433f5b1c 100644 --- a/app/src/main/res/layout/fragment_exams_tab.xml +++ b/app/src/main/res/layout/fragment_exams_tab.xml @@ -25,7 +25,7 @@ android:layout_height="match_parent" android:gravity="center"> - - \ No newline at end of file + diff --git a/app/src/main/res/layout/fragment_grades.xml b/app/src/main/res/layout/fragment_grades.xml index f00b3108..1f177a60 100644 --- a/app/src/main/res/layout/fragment_grades.xml +++ b/app/src/main/res/layout/fragment_grades.xml @@ -6,45 +6,166 @@ android:layout_height="match_parent" tools:context="io.github.wulkanowy.ui.main.grades.GradesFragment"> - - - - - - - - + android:layout_height="match_parent"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - \ No newline at end of file + diff --git a/app/src/main/res/layout/fragment_timetable_tab.xml b/app/src/main/res/layout/fragment_timetable_tab.xml index 47aa5101..612b5df8 100644 --- a/app/src/main/res/layout/fragment_timetable_tab.xml +++ b/app/src/main/res/layout/fragment_timetable_tab.xml @@ -25,7 +25,7 @@ android:layout_height="match_parent" android:gravity="center"> - diff --git a/app/src/main/res/layout/grade_dialog.xml b/app/src/main/res/layout/grades_dialog.xml similarity index 99% rename from app/src/main/res/layout/grade_dialog.xml rename to app/src/main/res/layout/grades_dialog.xml index 048f1e1b..c5cf073e 100644 --- a/app/src/main/res/layout/grade_dialog.xml +++ b/app/src/main/res/layout/grades_dialog.xml @@ -177,6 +177,7 @@ android:background="?attr/selectableItemBackground" android:focusable="true" android:text="@string/generic_dialog_close" + android:textColor="?android:attr/android:textColorSecondary" android:textAllCaps="true" android:textSize="15sp" /> diff --git a/app/src/main/res/layout/grade_header.xml b/app/src/main/res/layout/grades_header.xml similarity index 89% rename from app/src/main/res/layout/grade_header.xml rename to app/src/main/res/layout/grades_header.xml index 032ad675..b819dd54 100644 --- a/app/src/main/res/layout/grade_header.xml +++ b/app/src/main/res/layout/grades_header.xml @@ -29,7 +29,7 @@ android:layout_below="@+id/grade_header_subject_text" android:layout_marginTop="5dp" android:text="@string/app_name" - android:textColor="@color/secondary_text" + android:textColor="?android:attr/android:textColorSecondary" android:textSize="12sp" /> + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/grades_summary_subitem.xml b/app/src/main/res/layout/grades_summary_subitem.xml new file mode 100644 index 00000000..c7715092 --- /dev/null +++ b/app/src/main/res/layout/grades_summary_subitem.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/timetable_dialog.xml b/app/src/main/res/layout/timetable_dialog.xml index 017004ec..5898bc4a 100644 --- a/app/src/main/res/layout/timetable_dialog.xml +++ b/app/src/main/res/layout/timetable_dialog.xml @@ -48,7 +48,7 @@ android:layout_below="@+id/timetable_dialog_details" android:layout_marginTop="10dp" android:text="@string/timetable_dialog_description" - android:textColor="@color/colorPrimaryDark" + android:textColor="@color/colorPrimary" android:textSize="17sp" /> @@ -192,6 +192,7 @@ android:background="?attr/selectableItemBackground" android:focusable="true" android:text="@string/generic_dialog_close" + android:textColor="?android:attr/android:textColorSecondary" android:textAllCaps="true" android:textSize="15sp" /> diff --git a/app/src/main/res/layout/timetable_header.xml b/app/src/main/res/layout/timetable_header.xml index 1e8ecec8..53ea8ce4 100644 --- a/app/src/main/res/layout/timetable_header.xml +++ b/app/src/main/res/layout/timetable_header.xml @@ -39,7 +39,7 @@ android:layout_marginTop="5dp" android:maxLines="1" android:text="@string/app_name" - android:textColor="@color/secondary_text" + android:textColor="?android:attr/android:textColorSecondary" android:textSize="14sp" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/timetable_subitem.xml b/app/src/main/res/layout/timetable_subitem.xml index 86b173b4..3c3cdc42 100644 --- a/app/src/main/res/layout/timetable_subitem.xml +++ b/app/src/main/res/layout/timetable_subitem.xml @@ -13,7 +13,6 @@ android:foreground="?attr/selectableItemBackgroundBorderless" card_view:cardElevation="0dp"> - @@ -92,4 +91,4 @@ tool:ignore="contentDescription"/> - \ No newline at end of file + diff --git a/app/src/main/res/layout/timetable_widget.xml b/app/src/main/res/layout/timetable_widget.xml index c2f146ae..acf8d238 100644 --- a/app/src/main/res/layout/timetable_widget.xml +++ b/app/src/main/res/layout/timetable_widget.xml @@ -72,4 +72,4 @@ android:text="@string/widget_timetable_no_lesson" android:textColor="@android:color/black" android:textSize="20sp" /> - \ No newline at end of file + diff --git a/app/src/main/res/menu/grades_action_menu.xml b/app/src/main/res/menu/grades_action_menu.xml new file mode 100644 index 00000000..0533ae39 --- /dev/null +++ b/app/src/main/res/menu/grades_action_menu.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/app/src/main/res/menu/semester_switch.xml b/app/src/main/res/menu/semester_switch.xml deleted file mode 100644 index 6f8a027e..00000000 --- a/app/src/main/res/menu/semester_switch.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..d0b4f53f --- /dev/null +++ b/app/src/main/res/values-night/styles.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/app/src/main/res/values-pl/prefernces_array.xml b/app/src/main/res/values-pl/prefernces_array.xml index 4e66a373..3731be40 100644 --- a/app/src/main/res/values-pl/prefernces_array.xml +++ b/app/src/main/res/values-pl/prefernces_array.xml @@ -9,4 +9,10 @@ 12 godzin 24 godzin - \ No newline at end of file + + Wyłączony + Włączony + Automatyczny + Używaj ustawień systemowych + + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index aeb828d1..1af99630 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -81,6 +81,9 @@ Brak lekcji w tym tygodniu Sala %s + Przewidywana + Końcowa + %d ocena %d oceny @@ -126,6 +129,7 @@ Pokazuj podsumowanie w ocenach Pokazuj obecność we frekwencji Wymagany restart + Ciemny motyw (Beta) Powiadomienia Pokazuj powiadomienia @@ -154,4 +158,11 @@ Brak sprawdzianów w tym tygodniu Typ Data wpisu + + Obliczona średnia + Szacowana średnia + Końcowa średnia + Podsumowanie + Szczegóły + Podczas synchronizacji wystąpił błąd diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 00000000..3593e29e --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 90f8c15e..68145e05 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -10,8 +10,11 @@ #FFE68C #CE9AD2 #d32f2f + #cdcdcd + #4c4c4c #333 - #cdcdcd - #eee + + #303030 + #ffffff diff --git a/app/src/main/res/values/prefernces_array.xml b/app/src/main/res/values/prefernces_array.xml index 346b57c3..59f4fffe 100644 --- a/app/src/main/res/values/prefernces_array.xml +++ b/app/src/main/res/values/prefernces_array.xml @@ -12,6 +12,20 @@ 2 3 + + + Off + On + Auto + Follow system settings + + + 1 + 2 + 0 + -1 + + 10 minutes 30 minutes @@ -30,4 +44,4 @@ 720 1440 - \ No newline at end of file + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ffea541..21e63f5f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -80,6 +80,9 @@ Final: %1$s No lesson in this week + Predicted + Final + Room %s @@ -122,6 +125,8 @@ Show present in attendance Required restart + Dark theme (Beta) + Notifications Show the notifications @@ -149,4 +154,11 @@ No exams in this week Type Date of entry + + Calculated average + Predicted average + Final average + Summary + Details + An error has occurred during synchronization diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index d72afb53..1eb55f9d 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,6 +1,6 @@ - -