diff --git a/.circleci/config.yml b/.circleci/config.yml index fe74ebe66..387c5edd5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ references: container_config: &container_config docker: - - image: circleci/android:api-26-alpha + - image: circleci/android:api-27-alpha working_directory: *workspace_root environment: environment: @@ -87,11 +87,8 @@ jobs: name: Upload unit code coverage to codecov command: bash <(curl -s https://codecov.io/bash) -F app - store_artifacts: - path: ./app/build/reports/tests/ - destination: tests_reports/ - - store_artifacts: - path: ./app/build/reports/jacoco/jacocoTestDebugUnitTestReport/ - destination: coverage_reports/ + path: ./app/build/reports/ + destination: reports/ - store_test_results: path: ./app/build/test-results - persist_to_workspace: @@ -112,11 +109,8 @@ jobs: name: Upload code coverage to codecov command: bash <(curl -s https://codecov.io/bash) -F api - store_artifacts: - path: ./api/build/reports/tests/ - destination: tests_reports/ - - store_artifacts: - path: ./api/build/reports/jacoco/test/ - destination: coverage_reports/ + path: ./api/build/reports/ + destination: reports/ - store_test_results: path: ./api/build/test-results - persist_to_workspace: @@ -130,7 +124,7 @@ jobs: - *attach_workspace - run: name: Setup emulator - command: sdkmanager "system-images;android-19;google_apis;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-19;google_apis;armeabi-v7a" + command: sdkmanager "system-images;android-16;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-16;default;armeabi-v7a" - run: name: Launch emulator command: export LD_LIBRARY_PATH=${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/qt/lib && emulator64-arm -avd test -noaudio -no-boot-anim -no-window -accel on diff --git a/.gitignore b/.gitignore index 8ad04ebf0..4f2819d49 100644 --- a/.gitignore +++ b/.gitignore @@ -46,5 +46,5 @@ ehthumbs.db Thumbs.db .idea/codeStyles/ .idea/caches/ -./app/key.p12 -./app/upload-key.jks +app/key.p12 +app/upload-key.jks diff --git a/README.md b/README.md index 6ccaa20af..ee11a9c6d 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,6 @@ [Pobierz wersję beta](https://play.google.com/store/apps/details?id=io.github.wulkanowy&utm_source=vcs) -[Pobierz wersję rozwojową](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/app-debug-bitrise-signed.apk) +[Pobierz wersję DEV](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/0) Androidowy klient dziennika VULCAN UONET+. diff --git a/api/build.gradle b/api/build.gradle index 001d25791..5e5eeee37 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" 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 53300788a..c6360d04a 100644 --- a/api/src/main/java/io/github/wulkanowy/api/Client.java +++ b/api/src/main/java/io/github/wulkanowy/api/Client.java @@ -3,12 +3,16 @@ 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; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import io.github.wulkanowy.api.generic.School; import io.github.wulkanowy.api.login.Login; public class Client { @@ -23,14 +27,21 @@ public class Client { private String symbol; + private String schoolId; + + private List schools; + private Date lastSuccessRequest; private Cookies cookies = new Cookies(); - Client(String email, String password, String symbol) { + private static final Logger logger = LoggerFactory.getLogger(Client.class); + + Client(String email, String password, String symbol, String schoolId) { this.email = email; this.password = password; this.symbol = symbol; + this.schoolId = schoolId; setFullEndpointInfo(email); } @@ -58,10 +69,19 @@ public class Client { return; } - this.symbol = new Login(this).login(email, password, symbol); + logger.info("Not logged. Login..."); + + clearCookies(); + new Login(this).login(email, password, symbol); + lastSuccessRequest = new Date(); + + logger.info("Login successful on {} at {}", getHost(), new Date()); } private boolean isLoggedIn() { + logger.trace("Last success request: {}", lastSuccessRequest); + logger.trace("Cookies: {}", getCookies().size()); + return getCookies().size() > 0 && lastSuccessRequest != null && 5 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime()); @@ -75,23 +95,38 @@ public class Client { this.symbol = symbol; } - public void addCookies(Map items) { - cookies.addItems(items); - } - private Map getCookies() { return cookies.getItems(); } - String getHost() { + public void clearCookies() { + cookies = new Cookies(); + } + + public String getHost() { return host; } + public void setSchools(List schools) { + this.schools = schools; + this.schoolId = schools.get(0).getId(); + } + + public List getSchools() throws IOException, VulcanException { + login(); + return schools; + } + + public String getSchoolId() throws IOException, VulcanException { + return schoolId != null ? schoolId : getSchools().get(0).getId(); + } + String getFilledUrl(String url) { return url .replace("{schema}", protocol) - .replace("{host}", host.replace(":", "%253A")) - .replace("{symbol}", symbol); + .replace("{host}", host) + .replace("{symbol}", symbol) + .replace("{ID}", schoolId != null ? schoolId : ""); } public Document getPageByUrl(String url) throws IOException, VulcanException { @@ -111,14 +146,18 @@ 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(); this.cookies.addItems(response.cookies()); - Document doc = checkForErrors(response.parse()); + Document doc = checkForErrors(response.parse(), response.statusCode()); if (loginBefore) { lastSuccessRequest = new Date(); @@ -128,7 +167,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]); @@ -144,13 +187,17 @@ public class Client { response.bufferUp(); // fixes cert parsing issues #109 - return checkForErrors(response.parse()); + return checkForErrors(response.parse(), response.statusCode()); } 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 +211,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]); @@ -182,7 +233,7 @@ public class Client { return response.body(); } - Document checkForErrors(Document doc) throws VulcanException { + Document checkForErrors(Document doc, int code) throws VulcanException { lastSuccessRequest = null; String title = doc.select("title").text(); @@ -195,8 +246,8 @@ public class Client { throw new NotLoggedInErrorException(singIn); } - if (title.startsWith("Błąd")) { - throw new NotLoggedInErrorException(title + " " + doc.selectFirst("p, body")); + if ("Błąd strony".equals(title)) { + 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 000000000..3b620db50 --- /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 7f074071c..84ec1aed1 100644 --- a/api/src/main/java/io/github/wulkanowy/api/SnP.java +++ b/api/src/main/java/io/github/wulkanowy/api/SnP.java @@ -6,9 +6,12 @@ import org.jsoup.nodes.Element; import java.io.IOException; import java.util.List; -public interface SnP { +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; - String getSchoolID(); +public interface SnP { void setDiaryID(String id); 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 1acecfd3b..ba527f6a1 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,9 +13,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class StudentAndParent implements SnP { +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; - private static final String START_PAGE_URL = "{schema}://uonetplus.{host}/{symbol}/Start.mvc/Index"; +public class StudentAndParent implements SnP { private static final String BASE_URL = "{schema}://uonetplus-opiekun.{host}/{symbol}/{ID}/"; @@ -21,22 +26,26 @@ public class StudentAndParent implements SnP { private Client client; - private String schoolID; - private String studentID; private String diaryID; - StudentAndParent(Client client, String schoolID, String studentID, String diaryID) { + private static final Logger logger = LoggerFactory.getLogger(StudentAndParent.class); + + StudentAndParent(Client client, String studentID, String diaryID) { this.client = client; - this.schoolID = schoolID; this.studentID = studentID; this.diaryID = diaryID; } public StudentAndParent setUp() throws IOException, VulcanException { if (null == getStudentID() || "".equals(getStudentID())) { - Document doc = client.getPageByUrl(getSnpHomePageUrl()); + Document doc = client.getPageByUrl(BASE_URL); + + 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(); @@ -48,48 +57,10 @@ public class StudentAndParent implements SnP { return this; } - public String getSchoolID() { - return schoolID; - } - public String getStudentID() { return studentID; } - private String getBaseUrl() { - return BASE_URL.replace("{ID}", getSchoolID()); - } - - String getSnpHomePageUrl() throws IOException, VulcanException { - if (null != getSchoolID()) { - return getBaseUrl(); - } - - // get url to uonetplus-opiekun.vulcan.net.pl - Document startPage = client.getPageByUrl(START_PAGE_URL); - Element studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a").first(); - - if (null == studentTileLink) { - throw new NotLoggedInErrorException("You are probably not logged in. Force login"); - } - - String snpPageUrl = studentTileLink.attr("href"); - - this.schoolID = getExtractedIdFromUrl(snpPageUrl); - - return snpPageUrl; - } - - String getExtractedIdFromUrl(String snpPageUrl) throws NotLoggedInErrorException { - String[] path = snpPageUrl.split(client.getHost())[1].split("/"); - - if (5 != path.length) { - throw new NotLoggedInErrorException("You are probably not logged in"); - } - - return path[2]; - } - public String getRowDataChildValue(Element e, int index) { return e.select(".daneWiersz .wartosc").get(index - 1).text(); } @@ -102,12 +73,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); + Document doc = client.getPageByUrl(BASE_URL + 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")) { @@ -118,7 +89,7 @@ public class StudentAndParent implements SnP { } public List getDiaries() throws IOException, VulcanException { - return getDiaries(client.getPageByUrl(getBaseUrl())); + return getDiaries(client.getPageByUrl(BASE_URL)); } private List getDiaries(Document doc) throws IOException, VulcanException { @@ -126,7 +97,7 @@ public class StudentAndParent implements SnP { } public List getStudents() throws IOException, VulcanException { - return getStudents(client.getPageByUrl(getBaseUrl())); + return getStudents(client.getPageByUrl(BASE_URL)); } private List getStudents(Document doc) throws IOException, VulcanException { 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 d90d38741..bf482969c 100644 --- a/api/src/main/java/io/github/wulkanowy/api/Vulcan.java +++ b/api/src/main/java/io/github/wulkanowy/api/Vulcan.java @@ -1,13 +1,20 @@ package io.github.wulkanowy.api; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; +import java.util.List; import io.github.wulkanowy.api.attendance.AttendanceStatistics; import io.github.wulkanowy.api.attendance.AttendanceTable; import io.github.wulkanowy.api.exams.ExamsWeek; +import io.github.wulkanowy.api.generic.School; 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; @@ -22,23 +29,24 @@ public class Vulcan { private Client client; - private String schoolId; - private String studentId; 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); + client = new Client(email, password, symbol, schoolId); + + 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; @@ -46,7 +54,10 @@ public class Vulcan { public String getSymbol() throws NotLoggedInErrorException { return getClient().getSymbol(); + } + public List getSchools() throws VulcanException, IOException { + return getClient().getSchools(); } public SnP getStudentAndParent() throws VulcanException, IOException { @@ -54,7 +65,7 @@ public class Vulcan { return this.snp; } - this.snp = new StudentAndParent(getClient(), schoolId, studentId, diaryId) + this.snp = new StudentAndParent(getClient(), studentId, diaryId) .setUp(); return this.snp; @@ -108,6 +119,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 d81a30b2f..7caa70d80 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 c111ce848..391056ac9 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 5b9086a37..fbb5ee0a7 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 d7d09f9ce..5c7c8593b 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 c830467ad..e7edfbf48 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/generic/School.kt b/api/src/main/java/io/github/wulkanowy/api/generic/School.kt new file mode 100644 index 000000000..c14fc5d26 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/School.kt @@ -0,0 +1,7 @@ +package io.github.wulkanowy.api.generic + +data class School( + val name: String, + val id: String, + val current: Boolean +) 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 31230f012..db4a724dd 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 8b22f1d83..4ed5dd373 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 936c1533b..cb9fe4657 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 a734501ea..443a9387b 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,29 +5,40 @@ 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; import io.github.wulkanowy.api.Client; +import io.github.wulkanowy.api.NotLoggedInErrorException; import io.github.wulkanowy.api.VulcanException; public class Login { - static final String LOGIN_PAGE_URL = "{schema}://cufs.{host}/{symbol}/Account/LogOn" + - "?ReturnUrl=%2F{symbol}%2FFS%2FLS%3Fwa%3Dwsignin1.0%26wtrealm%3D" + + protected static final String LOGIN_PAGE_URL = "{schema}://cufs.{host}/{symbol}/Account/LogOn"; + + private static final String LOGIN_PAGE_URL_QUERY = "?ReturnUrl=%2F{symbol}%2FFS%2FLS%3Fwa%3Dwsignin1.0%26wtrealm%3D" + "{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx%26wctx%3D" + "{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx"; private Client client; + private static final Logger logger = LoggerFactory.getLogger(Login.class); + public Login(Client client) { this.client = client; } - public String login(String email, String password, String symbol) throws VulcanException, IOException { + public void login(String email, String password, String symbol) throws VulcanException, IOException { Document certDoc = sendCredentials(email, password); - return sendCertificate(certDoc, symbol); + if ("Błąd".equals(certDoc.title())) { + client.clearCookies(); + throw new NotLoggedInErrorException(certDoc.body().text()); + } + + sendCertificate(certDoc, symbol); } Document sendCredentials(String email, String password) throws IOException, VulcanException { @@ -36,7 +47,7 @@ public class Login { {"Password", password} }; - Document nextDoc = sendCredentialsData(credentials, LOGIN_PAGE_URL); + Document nextDoc = sendCredentialsData(credentials, LOGIN_PAGE_URL + LOGIN_PAGE_URL_QUERY.replace(":", "%253A")); Element errorMessage = nextDoc.selectFirst(".ErrorMessage, #ErrorTextLabel"); if (null != errorMessage) { @@ -76,13 +87,14 @@ public class Login { }; } - String sendCertificate(Document doc, String defaultSymbol) throws IOException, VulcanException { + void sendCertificate(Document doc, String defaultSymbol) throws IOException, VulcanException { client.setSymbol(findSymbol(defaultSymbol, doc.select("input[name=wresult]").val())); Document targetDoc = sendCertData(doc); String title = targetDoc.title(); if ("Working...".equals(title)) { // on adfs login + logger.info("ADFS login"); title = sendCertData(targetDoc).title(); } @@ -91,10 +103,11 @@ public class Login { } if (!"Uonet+".equals(title)) { + logger.debug("Login failed. Body: {}", targetDoc.body()); throw new LoginErrorException("Expected page title `UONET+`, got " + title); } - return client.getSymbol(); + client.setSchools(new StartPage(client).getSchools(targetDoc)); } private Document sendCertData(Document doc) throws IOException, VulcanException { diff --git a/api/src/main/java/io/github/wulkanowy/api/login/StartPage.kt b/api/src/main/java/io/github/wulkanowy/api/login/StartPage.kt new file mode 100644 index 000000000..57463a087 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/StartPage.kt @@ -0,0 +1,46 @@ +package io.github.wulkanowy.api.login + +import io.github.wulkanowy.api.Client +import io.github.wulkanowy.api.VulcanException +import io.github.wulkanowy.api.generic.School +import org.jsoup.nodes.Document +import org.slf4j.LoggerFactory +import java.net.URL + +class StartPage(val client: Client) { + + private val logger = LoggerFactory.getLogger(StartPage::class.java) + + fun getSchools(startPage: Document): MutableList { + val schoolList = mutableListOf() + + val snpLinks = startPage.select(".panel.linkownia.pracownik.klient a[href*=\"uonetplus-opiekun\"]") + + logger.debug("SnP links: {}", snpLinks.size) + + if (snpLinks.isEmpty()) { + throw VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?") + } + + snpLinks.map { + schoolList.add(School( + it.text(), + getExtractedSchoolSymbolFromUrl(it.attr("href")), + it == snpLinks.first() + )) + } + + return schoolList + } + + internal fun getExtractedSchoolSymbolFromUrl(snpPageUrl: String): String { + val path = URL(snpPageUrl).path.split("/") + + if (6 != path.size) { + logger.error("Expected snp url, got {}", snpPageUrl) + throw VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?") + } + + return path[2] + } +} 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 000000000..f0da01e9d --- /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 000000000..d622b4de3 --- /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 11d013429..346df832e 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 0c8a30b6b..54f57edf4 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 51a7278bd..f0dcd43de 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 bbf5f5d7f..ec8429bd9 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 77ca41bd6..95c40167c 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); @@ -98,8 +102,10 @@ public class Timetable { addLessonInfoFromElement(lesson, e.first()); break; case 2: - Element span = e.last().select("span").first(); - if (span.hasClass(LessonTypes.CLASS_MOVED_OR_CANCELED)) { + Element span = e.last().selectFirst("span"); + if (null == span) { + addLessonInfoFromElement(lesson, e.first()); + } else if (span.hasClass(LessonTypes.CLASS_MOVED_OR_CANCELED)) { lesson.setNewMovedInOrChanged(true); lesson.setDescription("poprzednio: " + getLessonAndGroupInfoFromSpan(span)[0]); addLessonInfoFromElement(lesson, e.first()); @@ -120,7 +126,9 @@ public class Timetable { Elements warn = e.select(".uwaga-panel"); if (!warn.isEmpty()) { - e.select(".x-treelabel-rlz").last().text("(" + warn.text() + ")"); + e.select("span").last() + .addClass("x-treelabel-rlz") + .text(warn.text()); e.remove(1); } } @@ -128,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); @@ -214,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/ClientTest.java b/api/src/test/java/io/github/wulkanowy/api/ClientTest.java index 38f856172..298affac1 100644 --- a/api/src/test/java/io/github/wulkanowy/api/ClientTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/ClientTest.java @@ -12,52 +12,67 @@ public class ClientTest { } @Test - public void setFullEndpointInfoTest() throws Exception { - Client client = new Client("http://fakelog.net\\\\admin", "pass", "Default"); + public void setFullEndpointInfoTest() { + Client client = new Client("http://fakelog.cf\\\\admin", "pass", "Default", "123"); - Assert.assertEquals("fakelog.net", client.getHost()); + Assert.assertEquals("fakelog.cf", client.getHost()); Assert.assertEquals("Default", client.getSymbol()); } + @Test + public void setFullEndpointInfoWithSymbolTest() { + Client client = new Client("http://fakelog.cf/notdefault\\\\admin", "pass", "Default", "123"); + + Assert.assertEquals("fakelog.cf", client.getHost()); + Assert.assertEquals("notdefault", client.getSymbol()); // + } + @Test public void checkForNoErrorsTest() throws Exception { - Client client = new Client("", "", ""); + Client client = new Client("", "", "", "123"); Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-success.html")); - Assert.assertEquals(doc, client.checkForErrors(doc)); + Assert.assertEquals(doc, client.checkForErrors(doc, 200)); } @Test(expected = VulcanOfflineException.class) public void checkForErrorsOffline() throws Exception { - Client client = new Client("", "", ""); + Client client = new Client("", "", "", "123"); Document doc = Jsoup.parse(getFixtureAsString("login/PrzerwaTechniczna.html")); - client.checkForErrors(doc); + client.checkForErrors(doc, 200); } @Test(expected = NotLoggedInErrorException.class) public void checkForErrors() throws Exception { - Client client = new Client("", "", ""); + Client client = new Client("", "", "", "123"); Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-notLoggedIn.html")); - client.checkForErrors(doc); + client.checkForErrors(doc, 200); } @Test public void getFilledUrlTest() throws Exception { - Client client = new Client("http://fakelog.cf\\\\admin", "", "symbol123"); + Client client = new Client("http://fakelog.cf\\\\admin", "", "symbol123", "321"); - Assert.assertEquals("http://uonetplus.fakelog.cf/symbol123/LoginEndpoint.aspx", - client.getFilledUrl("{schema}://uonetplus.{host}/{symbol}/LoginEndpoint.aspx")); + Assert.assertEquals("http://uonetplus-opiekun.fakelog.cf/symbol123/321/Oceny/Wszystkie", + client.getFilledUrl("{schema}://uonetplus-opiekun.{host}/{symbol}/{ID}/Oceny/Wszystkie")); } @Test - public void getSymbolTest() throws Exception { - Client client = new Client("", "", "symbol4321"); + public void getSymbolTest() { + Client client = new Client("", "", "symbol4321", "123"); Assert.assertEquals("symbol4321", client.getSymbol()); } + + @Test + public void getSchoolIdTest() throws Exception { + Client client = new Client("", "", "1", "123456"); + + Assert.assertEquals("123456", client.getSchoolId()); + } } 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 000000000..1105b2a72 --- /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 2b3d72500..4c6443b1e 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; @@ -30,68 +32,13 @@ public class StudentAndParentTest { @Test public void snpTest() { - StudentAndParent snp = new StudentAndParent(client, "id123", null, null); - Assert.assertEquals("id123", snp.getSchoolID()); - } - - @Test - public void getSnpPageUrlWithIdTest() throws Exception { - Assert.assertEquals("{schema}://uonetplus-opiekun.{host}/{symbol}/123456/", - (new StudentAndParent(client, "123456", null, null)).getSnpHomePageUrl()); - } - - @Test - public void getSnpPageUrlWithoutIdTest() throws Exception { - String input = FixtureHelper.getAsString(getClass().getResourceAsStream("Start.html")); - Document startPageDocument = Jsoup.parse(input); - - Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); - Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(startPageDocument); - StudentAndParent snp = new StudentAndParent(client, null, null, null); - - Assert.assertEquals("https://uonetplus-opiekun.vulcan.net.pl/symbol/534213/Start/Index/", - snp.getSnpHomePageUrl()); - } - - @Test(expected = NotLoggedInErrorException.class) - public void getSnpPageUrlWithWrongPage() throws Exception { - Document wrongPageDocument = Jsoup.parse( - FixtureHelper.getAsString(getClass().getResourceAsStream("OcenyWszystkie-semester.html")) - ); - - Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(wrongPageDocument); - StudentAndParent snp = new StudentAndParent(client, null, null, null); - - snp.getSnpHomePageUrl(); - } - - @Test - public void getExtractedIDStandardTest() throws Exception { - Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); - StudentAndParent snp = new StudentAndParent(client, "symbol", null, null); - Assert.assertEquals("123456", snp.getExtractedIdFromUrl("https://uonetplus-opiekun" - + ".vulcan.net.pl/powiat/123456/Start/Index/")); - } - - @Test - public void getExtractedIDDemoTest() throws Exception { - Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); - StudentAndParent snp = new StudentAndParent(client, "symbol", null, null); - Assert.assertEquals("demo12345", - snp.getExtractedIdFromUrl("https://uonetplus-opiekun.vulcan.net.pl/demoupowiat/demo12345/Start/Index/")); - } - - @Test(expected = NotLoggedInErrorException.class) - public void getExtractedIDNotLoggedTest() throws Exception { - Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); - StudentAndParent snp = new StudentAndParent(client, "symbol", null, null); - Assert.assertEquals("123", - snp.getExtractedIdFromUrl("https://uonetplus.vulcan.net.pl/powiat/")); + StudentAndParent snp = new StudentAndParent(client, "1234", null); + Assert.assertEquals("1234", snp.getStudentID()); } @Test public void getSemestersTest() throws Exception { - SnP snp = new StudentAndParent(client, "123456", null, null); + SnP snp = new StudentAndParent(client, null, null); List semesters = snp.getSemesters(); Assert.assertEquals(2, semesters.size()); @@ -111,7 +58,7 @@ public class StudentAndParentTest { semesters.add(new Semester().setName("1500100900").setId("1").setCurrent(false)); semesters.add(new Semester().setName("1500100901").setId("2").setCurrent(true)); - SnP snp = new StudentAndParent(client, "", null, null); + SnP snp = new StudentAndParent(client, null, null); Semester semester = snp.getCurrent(semesters); Assert.assertTrue(semester.isCurrent()); @@ -121,7 +68,7 @@ public class StudentAndParentTest { @Test public void getCurrentSemesterFromEmptyTest() { - SnP snp = new StudentAndParent(client, "", null, null); + SnP snp = new StudentAndParent(client, null, null); List semesters = new ArrayList<>(); Assert.assertNull(snp.getCurrent(semesters)); @@ -129,12 +76,12 @@ 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); - SnP snp = new StudentAndParent(client, "", null, null); + SnP snp = new StudentAndParent(client, null, null); snp.setUp(); 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 8d32291fe..764e8009e 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 6eff84c95..deab7617c 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/login/LoginTest.java b/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java index 4a4f8915a..86a3ef409 100644 --- a/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java +++ b/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java @@ -38,9 +38,11 @@ public class LoginTest { .thenReturn(getFixtureAsDocument("Logowanie-certyfikat.html")); Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString()); Mockito.when(client.getSymbol()).thenCallRealMethod(); + Mockito.when(client.getHost()).thenReturn("fakelog.cf"); Login login = new Login(client); + login.login("a@a", "pswd", "d123"); - Assert.assertEquals("d123", login.login("a@a", "pswd", "d123")); + Assert.assertEquals("d123", client.getSymbol()); } @Test(expected = BadCredentialsException.class) @@ -74,10 +76,12 @@ public class LoginTest { Client client = getClient("Logowanie-success.html"); Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString()); Mockito.when(client.getSymbol()).thenCallRealMethod(); + Mockito.when(client.getHost()).thenReturn("fakelog.cf"); Login login = new Login(client); - Assert.assertEquals("wulkanowyschool321", login.sendCertificate( - getFixtureAsDocument("Logowanie-certyfikat.html"), "wulkanowyschool321")); + login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "wulkanowyschool321"); + + Assert.assertEquals("wulkanowyschool321", client.getSymbol()); } @Test @@ -85,10 +89,12 @@ public class LoginTest { Client client = getClient("Logowanie-success.html"); Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString()); Mockito.when(client.getSymbol()).thenCallRealMethod(); + Mockito.when(client.getHost()).thenReturn("fakelog.cf"); Login login = new Login(client); - Assert.assertEquals("demo12345", - login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "Default")); + login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "Default"); + + Assert.assertEquals("demo12345", client.getSymbol()); } @Test(expected = AccountPermissionException.class) diff --git a/api/src/test/java/io/github/wulkanowy/api/login/StartPageTest.kt b/api/src/test/java/io/github/wulkanowy/api/login/StartPageTest.kt new file mode 100644 index 000000000..fb2bf00ca --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/login/StartPageTest.kt @@ -0,0 +1,68 @@ +package io.github.wulkanowy.api.login + +import io.github.wulkanowy.api.Client +import io.github.wulkanowy.api.FixtureHelper +import io.github.wulkanowy.api.VulcanException +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito +import org.mockito.Mockito.mock + +class StartPageTest { + + private val client: Client = mock(Client::class.java) + + @Before fun setUp() { + Mockito.`when`(client.host).thenReturn("fakelog.cf") + } + + private fun getDoc(name: String): Document = Jsoup.parse(FixtureHelper.getAsString(javaClass.getResourceAsStream(name))) + + @Test fun getSchoolTest() { + assertEquals("534213", StartPage(client).getSchools(getDoc("../Start-std.html"))[0].id) + } + + @Test fun getMultiSchoolTest() { + val schools = StartPage(client).getSchools(getDoc("../Start-multi.html")) + + assertEquals("123456", schools[0].id) + assertEquals("123457", schools[1].id) + } + + @Test fun getSchoolNameTest() { + assertEquals("Uczeń", StartPage(client).getSchools(getDoc("../Start-std.html"))[0].name) + } + + @Test fun getMultiSchoolNameTest() { + val schools = StartPage(client).getSchools(getDoc("../Start-multi.html")) + + assertEquals("GIMBB", schools[0].name) + assertEquals("SPBB", schools[1].name) + } + + @Test(expected = VulcanException::class) + fun getSnpPageUrlWithWrongPage() { + StartPage(client).getSchools(getDoc("../OcenyWszystkie-semester.html")) + } + + @Test + fun getExtractedIDStandardTest() { + assertEquals("123456", StartPage(client) + .getExtractedSchoolSymbolFromUrl("https://uonetplus-opiekun.fakelog.cf/powiat/123456/Start/Index/")) + } + + @Test + fun getExtractedIDDemoTest() { + assertEquals("demo12345", StartPage(client) + .getExtractedSchoolSymbolFromUrl("https://uonetplus-opiekun.fakelog.cf/demoupowiat/demo12345/Start/Index/")) + } + + @Test(expected = VulcanException::class) + fun getExtractedIDNotLoggedTest() { + assertEquals("123", StartPage(client) + .getExtractedSchoolSymbolFromUrl("https://uonetplus.NOTfakelog.cf/powiat/")) + } +} 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 000000000..f67310c88 --- /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 000000000..3cd8b97cc --- /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 d76c06486..304eb01a8 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 8939b10fd..7b3cce6ee 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 @@ -105,6 +105,7 @@ public class TimetableTest extends StudentAndParentTestCase { Assert.assertEquals("Zajęcia techniczne", std.getWeekTable().getDay(2).getLesson(4).getSubject()); Assert.assertEquals("Wychowanie fizyczne", std.getWeekTable().getDay(1).getLesson(1).getSubject()); Assert.assertEquals("Język angielski", full.getWeekTable().getDay(0).getLesson(1).getSubject()); + Assert.assertEquals("Wychowanie fizyczne", full.getWeekTable().getDay(0).getLesson(9).getSubject()); Assert.assertEquals("Wychowanie do życia w rodzinie", full.getWeekTable().getDay(2).getLesson(0).getSubject()); Assert.assertEquals("Wychowanie fizyczne", full.getWeekTable().getDay(3).getLesson(1).getSubject()); Assert.assertEquals("Uroczyste zakończenie roku szkolnego", full.getWeekTable().getDay(4).getLesson(0).getSubject()); @@ -155,6 +156,8 @@ 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/Start-multi.html b/api/src/test/resources/io/github/wulkanowy/api/Start-multi.html new file mode 100644 index 000000000..d53236777 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/Start-multi.html @@ -0,0 +1,29 @@ + + + + + Uonet+ + + +
+
+
+
+ +
+
+
+
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/Start-std.html b/api/src/test/resources/io/github/wulkanowy/api/Start-std.html new file mode 100644 index 000000000..2cf03d630 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/Start-std.html @@ -0,0 +1,37 @@ + + + + + Uonet+ + + +
+ +
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/Start.html b/api/src/test/resources/io/github/wulkanowy/api/Start.html deleted file mode 100644 index 40e691610..000000000 --- a/api/src/test/resources/io/github/wulkanowy/api/Start.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - Uonet+ - - -
- -
- - 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/login/Logowanie-success.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-success.html index 23c27a3fe..b635388b3 100644 --- a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-success.html +++ b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-success.html @@ -11,6 +11,21 @@ example@wulkanowy.io (wyloguj) +
+ +
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 000000000..9fbfd4039 --- /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 000000000..15d08d07c --- /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 4bce6c935..c3dbfebe0 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 @@ -319,7 +319,16 @@ - + +
+ Język polski + + 16 + (oddział nieobecny) +
+ +
opis w uwadze bez klasy w spanie
+ 5 @@ -355,7 +364,20 @@ - + +
+ Tworzenie i administrowanie bazami danych [zaw2] + + + +
+
+ Zajęcia z wychowawcą + Małgorzata Kowal + 43 + (zmiana organizacji zajęć) +
+ 6 @@ -461,7 +483,18 @@ 9 14:50 15:35 - + +
+ Wychowanie fizyczne [zaw2] + + + G3 + (przeniesiona z lekcji 7, 01.12.2017) +
+
+ +
+
Język niemiecki [J1] diff --git a/android-sonarqube.gradle b/app/android-sonarqube.gradle similarity index 97% rename from android-sonarqube.gradle rename to app/android-sonarqube.gradle index 3ad2c5ff3..b9284eeac 100644 --- a/android-sonarqube.gradle +++ b/app/android-sonarqube.gradle @@ -5,7 +5,7 @@ sonarqube { //noinspection GroovyAssignabilityCheck properties { def files = fileTree("${rootProject.projectDir}/api/build/libs/").filter { it.isFile() }.files.name - def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-26/android.jar," + + def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-27/android.jar," + "${project.rootDir}/api/build/libs/" + files[0] property "sonar.projectName", GROUP_ID + ":app" diff --git a/app/build.gradle b/app/build.gradle index 8a831d4f4..8a0abe3bd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,3 +1,13 @@ +apply plugin: 'org.greenrobot.greendao' +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' // sync warning probably caused by bug https://issuetracker.google.com/issues/74537216 +apply plugin: 'io.fabric' +apply from: 'jacoco.gradle' +apply from: 'android-sonarqube.gradle' +apply plugin: 'com.google.gms.oss.licenses.plugin' +apply plugin: 'com.github.triplet.play' + buildscript { repositories { maven { url "https://plugins.gradle.org/m2/" } @@ -9,7 +19,7 @@ buildscript { classpath "org.greenrobot:greendao-gradle-plugin:$greenDaoGradle" classpath "io.fabric.tools:gradle:$fabricGradle" classpath "com.google.gms:oss-licenses:0.9.2" - classpath 'com.github.triplet.gradle:play-publisher:1.2.0' + classpath "com.github.triplet.gradle:play-publisher:$playPublisher" } } @@ -17,16 +27,8 @@ repositories { maven { url 'https://maven.fabric.io/public' } } -apply plugin: 'com.android.application' -apply plugin: 'org.greenrobot.greendao' -apply plugin: 'io.fabric' -apply from: '../jacoco.gradle' -apply from: '../android-sonarqube.gradle' -apply plugin: 'com.google.gms.oss.licenses.plugin' -apply plugin: 'com.github.triplet.play' - android { - compileSdkVersion 26 + compileSdkVersion 27 buildToolsVersion '27.0.3' playAccountConfigs { @@ -40,9 +42,9 @@ android { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 - targetSdkVersion 26 - versionCode 11 - versionName "0.4.3" + targetSdkVersion 27 + versionCode 18 + versionName "0.5.4" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true playAccountConfig = playAccountConfigs.defaultAccountConfig @@ -91,7 +93,7 @@ play { } greendao { - schemaVersion 28 + schemaVersion 29 generateTests = true } @@ -101,6 +103,7 @@ configurations.all { dependencies { implementation project(':api') + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "com.android.support:support-v4:$supportVersion" implementation "com.android.support:design:$supportVersion" implementation "com.android.support:cardview-v7:$supportVersion" @@ -117,6 +120,8 @@ dependencies { implementation "com.aurelhubert:ahbottomnavigation:$ahbottom" 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 @@ -125,12 +130,11 @@ dependencies { transitive = true } - annotationProcessor "com.google.dagger:dagger-android-processor:$dagger2" - annotationProcessor "com.google.dagger:dagger-compiler:$dagger2" - annotationProcessor "com.jakewharton:butterknife-compiler:$butterknife" + kapt "com.google.dagger:dagger-compiler:$dagger2" + kapt "com.google.dagger:dagger-android-processor:$dagger2" + kapt "com.jakewharton:butterknife-compiler:$butterknife" debugImplementation "com.amitshekhar.android:debug-db:$debugDb" - debugImplementation "net.zetetic:android-database-sqlcipher:$sqlcipher" testImplementation "junit:junit:$junit" testImplementation "org.mockito:mockito-core:$mockito" diff --git a/app/jacoco.gradle b/app/jacoco.gradle new file mode 100644 index 000000000..f59b3857e --- /dev/null +++ b/app/jacoco.gradle @@ -0,0 +1,56 @@ +apply plugin: "jacoco" + +jacoco { + toolVersion "0.8.1" + reportsDir = file("$buildDir/reports") +} + +tasks.withType(Test) { + jacoco.includeNoLocationClasses = true +} + +// run ./gradlew clean createDebugCoverageReport jacocoTestReport +task jacocoTestReport(type: JacocoReport) { + + group = "Reporting" + description = "Generate Jacoco coverage reports" + + reports { + xml.enabled = true + html.enabled = true + } + + def excludes = [ + "**/R.class", + "**/R\$*.class", + "**/*\$ViewInjector*.*", + "**/BuildConfig.*", + "**/Manifest*.*", + "**/*Test*.*", + "android/**/*.*", + "**/*Fragment.*", + "**/*Activity.*" + ] + + // generated classes + classDirectories = fileTree( + // Java generated classes on Android project (debug build) + dir: "$buildDir/intermediates/classes/debug", + excludes: excludes + ) + fileTree( + // Kotlin generated classes on Android project (debug build) + dir: "$buildDir/tmp/kotlin-classes/debug", + excludes: excludes + ) + + // sources + sourceDirectories = files([ + android.sourceSets.main.java.srcDirs, + "src/main/kotlin" + ]) + + executionData = fileTree( + dir: project.projectDir, + includes: ["**/*.exec" , "**/*.ec"] + ) +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SchoolTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SchoolTest.java new file mode 100644 index 000000000..13f63e0d0 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SchoolTest.java @@ -0,0 +1,22 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +import io.github.wulkanowy.data.db.dao.entities.School; +import io.github.wulkanowy.data.db.dao.entities.SchoolDao; + +public class SchoolTest extends AbstractDaoTestLongPk { + + public SchoolTest() { + super(SchoolDao.class); + } + + @Override + protected School createEntity(Long key) { + School entity = new School(); + entity.setId(key); + entity.setCurrent(false); + return entity; + } + +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 24b5e1888..dd982626a 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 1516c0279..ac6a8b42c 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 ffcfb0466..e4dbd26b8 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 38115cc45..22eaa9445 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 @@ -4,6 +4,7 @@ import java.util.List; import io.github.wulkanowy.data.db.dao.entities.Grade; import io.github.wulkanowy.data.db.dao.entities.Subject; +import io.github.wulkanowy.data.db.dao.entities.Symbol; import io.github.wulkanowy.data.db.dao.entities.Week; public interface DbContract { @@ -16,10 +17,14 @@ public interface DbContract { List getNewGrades(int semesterName); + long getCurrentSchoolId(); + long getCurrentStudentId(); long getCurrentSymbolId(); + Symbol getCurrentSymbol(); + long getCurrentDiaryId(); long getSemesterId(int name); @@ -27,4 +32,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 97bb75598..33b193115 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; @@ -20,10 +21,9 @@ import io.github.wulkanowy.data.db.dao.migrations.Migration23; 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.dao.migrations.Migration29; 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 +33,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 +42,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 +55,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 +68,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); @@ -80,6 +80,7 @@ public class DbHelper extends DaoMaster.OpenHelper { migrations.add(new Migration26()); migrations.add(new Migration27()); migrations.add(new Migration28()); + migrations.add(new Migration29()); // Sorting just to be safe, in case other people add migrations in the wrong order. Comparator migrationComparator = new Comparator() { 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 6757c48e7..d869bfe9a 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,17 +1,22 @@ 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; import io.github.wulkanowy.data.db.dao.entities.GradeDao; +import io.github.wulkanowy.data.db.dao.entities.SchoolDao; import io.github.wulkanowy.data.db.dao.entities.Semester; import io.github.wulkanowy.data.db.dao.entities.SemesterDao; import io.github.wulkanowy.data.db.dao.entities.StudentDao; import io.github.wulkanowy.data.db.dao.entities.Subject; +import io.github.wulkanowy.data.db.dao.entities.Symbol; import io.github.wulkanowy.data.db.dao.entities.SymbolDao; import io.github.wulkanowy.data.db.dao.entities.Week; import io.github.wulkanowy.data.db.dao.entities.WeekDao; @@ -57,16 +62,29 @@ public class DbRepository implements DbContract { } @Override - public long getCurrentSymbolId() { + public Symbol getCurrentSymbol() { return daoSession.getSymbolDao().queryBuilder().where( SymbolDao.Properties.UserId.eq(sharedPref.getCurrentUserId()) + ).unique(); + } + + @Override + public long getCurrentSymbolId() { + return getCurrentSymbol().getId(); + } + + @Override + public long getCurrentSchoolId() { + return daoSession.getSchoolDao().queryBuilder().where( + SchoolDao.Properties.SymbolId.eq(getCurrentSymbolId()), + SchoolDao.Properties.Current.eq(true) ).unique().getId(); } @Override public long getCurrentStudentId() { return daoSession.getStudentDao().queryBuilder().where( - StudentDao.Properties.SymbolId.eq(getCurrentSymbolId()), + StudentDao.Properties.SchoolId.eq(getCurrentSchoolId()), StudentDao.Properties.Current.eq(true) ).unique().getId(); } @@ -103,4 +121,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/entities/School.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/School.java new file mode 100644 index 000000000..59860e9eb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/School.java @@ -0,0 +1,179 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.DaoException; +import org.greenrobot.greendao.annotation.Entity; +import org.greenrobot.greendao.annotation.Generated; +import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.Property; +import org.greenrobot.greendao.annotation.ToMany; + +import java.util.List; + +@Entity( + nameInDb = "Schools", + active = true +) +public class School { + + @Id(autoincrement = true) + private Long id; + + @Property(nameInDb = "symbol_id") + private Long symbolId; + + @Property(nameInDb = "current") + private boolean current; + + @Property(nameInDb = "real_id") + private String realId; + + @Property(nameInDb = "name") + private String name; + + @ToMany(referencedJoinProperty = "schoolId") + private List studentList; + + /** + * Used to resolve relations + */ + @Generated(hash = 2040040024) + private transient DaoSession daoSession; + + /** + * Used for active entity operations. + */ + @Generated(hash = 1796006707) + private transient SchoolDao myDao; + + @Generated(hash = 975562398) + public School(Long id, Long symbolId, boolean current, String realId, + String name) { + this.id = id; + this.symbolId = symbolId; + this.current = current; + this.realId = realId; + this.name = name; + } + + @Generated(hash = 1579966795) + public School() { + } + + public Long getId() { + return this.id; + } + + public School setId(Long id) { + this.id = id; + return this; + } + + public Long getSymbolId() { + return this.symbolId; + } + + public School setSymbolId(Long symbolId) { + this.symbolId = symbolId; + return this; + } + + public boolean getCurrent() { + return this.current; + } + + public School setCurrent(boolean current) { + this.current = current; + return this; + } + + public String getRealId() { + return this.realId; + } + + public School setRealId(String realId) { + this.realId = realId; + return this; + } + + public String getName() { + return this.name; + } + + public School setName(String name) { + this.name = name; + return this; + } + + /** + * To-many relationship, resolved on first access (and after reset). + * Changes to to-many relations are not persisted, make changes to the target entity. + */ + @Generated(hash = 180118651) + public List getStudentList() { + if (studentList == null) { + final DaoSession daoSession = this.daoSession; + if (daoSession == null) { + throw new DaoException("Entity is detached from DAO context"); + } + StudentDao targetDao = daoSession.getStudentDao(); + List studentListNew = targetDao._querySchool_StudentList(id); + synchronized (this) { + if (studentList == null) { + studentList = studentListNew; + } + } + } + return studentList; + } + + /** + * Resets a to-many relationship, making the next get call to query for a fresh result. + */ + @Generated(hash = 1628625923) + public synchronized void resetStudentList() { + studentList = null; + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 128553479) + public void delete() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.delete(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 1942392019) + public void refresh() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.refresh(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 713229351) + public void update() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.update(this); + } + + /** called by internal mechanisms, do not call yourself. */ + @Generated(hash = 234091322) + public void __setDaoSession(DaoSession daoSession) { + this.daoSession = daoSession; + myDao = daoSession != null ? daoSession.getSchoolDao() : null; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Student.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Student.java index f8ae0e312..1d5454732 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Student.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Student.java @@ -18,8 +18,8 @@ public class Student { @Id(autoincrement = true) private Long id; - @Property(nameInDb = "symbol_id") - private Long symbolId; + @Property(nameInDb = "school_id") + private Long schoolId; @Property(nameInDb = "current") private boolean current; @@ -45,10 +45,10 @@ public class Student { @Generated(hash = 1943931642) private transient StudentDao myDao; - @Generated(hash = 1334215952) - public Student(Long id, Long symbolId, boolean current, String realId, String name) { + @Generated(hash = 470181623) + public Student(Long id, Long schoolId, boolean current, String realId, String name) { this.id = id; - this.symbolId = symbolId; + this.schoolId = schoolId; this.current = current; this.realId = realId; this.name = name; @@ -66,12 +66,12 @@ public class Student { this.id = id; } - public Long getSymbolId() { - return this.symbolId; + public Long getSchoolId() { + return this.schoolId; } - public Student setSymbolId(Long symbolId) { - this.symbolId = symbolId; + public Student setSchoolId(Long schoolId) { + this.schoolId = schoolId; return this; } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Symbol.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Symbol.java index 2b5984b5c..7cc461112 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Symbol.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Symbol.java @@ -24,9 +24,6 @@ public class Symbol { @Property(nameInDb = "host") private String host; - @Property(nameInDb = "school_id") - private String schoolId; - @Property(nameInDb = "symbol") private String symbol; @@ -34,7 +31,7 @@ public class Symbol { private String type; @ToMany(referencedJoinProperty = "symbolId") - private List studentList; + private List schoolList; /** * Used to resolve relations @@ -48,13 +45,11 @@ public class Symbol { @Generated(hash = 684907977) private transient SymbolDao myDao; - @Generated(hash = 242774339) - public Symbol(Long id, Long userId, String host, String schoolId, String symbol, - String type) { + @Generated(hash = 1034469460) + public Symbol(Long id, Long userId, String host, String symbol, String type) { this.id = id; this.userId = userId; this.host = host; - this.schoolId = schoolId; this.symbol = symbol; this.type = type; } @@ -89,15 +84,6 @@ public class Symbol { return this; } - public String getSchoolId() { - return this.schoolId; - } - - public Symbol setSchoolId(String schoolId) { - this.schoolId = schoolId; - return this; - } - public String getSymbol() { return this.symbol; } @@ -120,30 +106,28 @@ public class Symbol { * To-many relationship, resolved on first access (and after reset). * Changes to to-many relations are not persisted, make changes to the target entity. */ - @Generated(hash = 604366458) - public List getStudentList() { - if (studentList == null) { + @Generated(hash = 1733082867) + public List getSchoolList() { + if (schoolList == null) { final DaoSession daoSession = this.daoSession; if (daoSession == null) { throw new DaoException("Entity is detached from DAO context"); } - StudentDao targetDao = daoSession.getStudentDao(); - List studentListNew = targetDao._querySymbol_StudentList(id); + SchoolDao targetDao = daoSession.getSchoolDao(); + List schoolListNew = targetDao._querySymbol_SchoolList(id); synchronized (this) { - if (studentList == null) { - studentList = studentListNew; + if (schoolList == null) { + schoolList = schoolListNew; } } } - return studentList; + return schoolList; } - /** - * Resets a to-many relationship, making the next get call to query for a fresh result. - */ - @Generated(hash = 1628625923) - public synchronized void resetStudentList() { - studentList = null; + /** Resets a to-many relationship, making the next get call to query for a fresh result. */ + @Generated(hash = 1757777300) + public synchronized void resetSchoolList() { + schoolList = null; } /** 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 b32c6261b..b2a41ad15 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/dao/migrations/Migration27.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration27.java index 5c8c82f1d..922079f40 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration27.java +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration27.java @@ -15,7 +15,7 @@ public class Migration27 implements DbHelper.Migration { } @Override - public void runMigration(Database db, SharedPrefContract sharedPref, Vulcan vulcan) throws Exception { + public void runMigration(Database db, SharedPrefContract sharedPref, Vulcan vulcan) { ExamDao.dropTable(db, true); ExamDao.createTable(db, true); diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration29.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration29.kt new file mode 100644 index 000000000..e318b5fd0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration29.kt @@ -0,0 +1,60 @@ +package io.github.wulkanowy.data.db.dao.migrations + +import android.database.Cursor + +import org.greenrobot.greendao.database.Database + +import io.github.wulkanowy.api.Vulcan +import io.github.wulkanowy.data.db.dao.DbHelper +import io.github.wulkanowy.data.db.shared.SharedPrefContract + +class Migration29 : DbHelper.Migration { + + override fun getVersion(): Int? { + return 29 + } + + override fun runMigration(db: Database, sharedPref: SharedPrefContract, vulcan: Vulcan) { + createSchoolsTable(db) + modifyStudents(db) + insertSchool(db, getRealSchoolId(db)) + } + + private fun createSchoolsTable(db: Database) { + db.execSQL("DROP TABLE IF EXISTS \"Schools\";") + db.execSQL("CREATE TABLE IF NOT EXISTS \"Schools\" (" + // + "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id + "\"symbol_id\" INTEGER," + // 1: symbolId + "\"current\" INTEGER NOT NULL ," + // 2: current + "\"real_id\" TEXT," + // 3: realId + "\"name\" TEXT);") // 4: name + } + + private fun modifyStudents(db: Database) { + db.execSQL("ALTER TABLE Students ADD COLUMN school_id INTEGER") + db.execSQL("UPDATE Students SET school_id = '1'") + } + + private fun getRealSchoolId(db: Database): String { + var cursor: Cursor? = null + try { + cursor = db.rawQuery("SELECT school_id FROM Symbols WHERE _id=?", arrayOf("1")) + + return if (cursor!!.count > 0) { + cursor.moveToFirst() + cursor.getString(cursor.getColumnIndex("school_id")) + } else "" + } finally { + cursor!!.close() + } + } + + private fun insertSchool(db: Database, realId: String) { + db.execSQL("INSERT INTO Schools(symbol_id, current, real_id, name) VALUES(" + + "\"1\"," + + "\"1\"," + + "\"" + realId + "\"," + + "\"Uczeń\"" + + ")") + } +} 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 a430e3355..f8a1baaa2 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 b9d3fd14c..370f8dfe1 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 bf46ce35a..adedbda4e 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 a79540a66..45f8c1f59 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 @@ -2,6 +2,8 @@ package io.github.wulkanowy.data.sync; import android.content.Context; +import org.greenrobot.greendao.database.Database; + import java.io.IOException; import java.util.List; @@ -11,20 +13,22 @@ import javax.inject.Singleton; import io.github.wulkanowy.api.Vulcan; import io.github.wulkanowy.api.VulcanException; import io.github.wulkanowy.data.db.dao.entities.Account; +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.Diary; import io.github.wulkanowy.data.db.dao.entities.DiaryDao; +import io.github.wulkanowy.data.db.dao.entities.School; +import io.github.wulkanowy.data.db.dao.entities.SchoolDao; import io.github.wulkanowy.data.db.dao.entities.Semester; import io.github.wulkanowy.data.db.dao.entities.Student; 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 { @@ -39,7 +43,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; @@ -49,16 +53,21 @@ public class AccountSync { public void registerUser(String email, String password, String symbol) throws VulcanException, IOException, CryptoException { + clearUserData(); + vulcan.setCredentials(email, password, symbol, null, null, null); daoSession.getDatabase().beginTransaction(); + Timber.i("Register start"); + try { Account account = insertAccount(email, password); Symbol symbolEntity = insertSymbol(account); - insertStudents(symbolEntity); - insertDiaries(symbolEntity); - insertSemesters(); + School schoolEntity = insertSchools(symbolEntity); + Student student = insertStudents(schoolEntity); + Diary diary = insertDiaries(student); + insertSemesters(diary); sharedPref.setCurrentUserId(account.getId()); @@ -66,10 +75,12 @@ public class AccountSync { } finally { daoSession.getDatabase().endTransaction(); } + + Timber.i("Register end"); } 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)); @@ -78,43 +89,64 @@ public class AccountSync { } private Symbol insertSymbol(Account account) throws VulcanException, IOException { - LogUtils.debug("Register symbol: " + vulcan.getSymbol()); + vulcan.getSchools(); + Timber.d("Register symbol (%s)", vulcan.getSymbol()); Symbol symbol = new Symbol() .setUserId(account.getId()) - .setSchoolId(vulcan.getStudentAndParent().getSchoolID()) .setSymbol(vulcan.getSymbol()); daoSession.getSymbolDao().insert(symbol); return symbol; } - private void insertStudents(Symbol symbol) throws VulcanException, IOException { - List studentList = DataObjectConverter.studentsToStudentEntities( - vulcan.getStudentAndParent().getStudents(), + private School insertSchools(Symbol symbol) throws VulcanException, IOException { + List schoolList = DataObjectConverter.schoolsToSchoolsEntities( + vulcan.getSchools(), symbol.getId() ); - LogUtils.debug("Register students: " + studentList.size()); - daoSession.getStudentDao().insertInTx(studentList); + Timber.d("Register schools (%s)", schoolList.size()); + daoSession.getSchoolDao().insertInTx(schoolList); + + return daoSession.getSchoolDao().queryBuilder().where( + SchoolDao.Properties.SymbolId.eq(symbol.getId()), + SchoolDao.Properties.Current.eq(true) + ).unique(); } - private void insertDiaries(Symbol symbolEntity) throws VulcanException, IOException { + private Student insertStudents(School school) throws VulcanException, IOException { + List studentList = DataObjectConverter.studentsToStudentEntities( + vulcan.getStudentAndParent().getStudents(), + school.getId() + ); + Timber.d("Register students (%s)", studentList.size()); + daoSession.getStudentDao().insertInTx(studentList); + + return daoSession.getStudentDao().queryBuilder().where( + StudentDao.Properties.SchoolId.eq(school.getId()), + StudentDao.Properties.Current.eq(true) + ).unique(); + } + + private Diary insertDiaries(Student student) throws VulcanException, IOException { List diaryList = DataObjectConverter.diariesToDiaryEntities( vulcan.getStudentAndParent().getDiaries(), - daoSession.getStudentDao().queryBuilder().where( - StudentDao.Properties.SymbolId.eq(symbolEntity.getId()), - StudentDao.Properties.Current.eq(true) - ).unique().getId()); - LogUtils.debug("Register diaries: " + diaryList.size()); + student.getId() + ); + Timber.d("Register diaries (%s)", diaryList.size()); daoSession.getDiaryDao().insertInTx(diaryList); + + return daoSession.getDiaryDao().queryBuilder().where( + DiaryDao.Properties.StudentId.eq(student.getId()), + DiaryDao.Properties.Current.eq(true) + ).unique(); } - private void insertSemesters() throws VulcanException, IOException { + private void insertSemesters(Diary diary) throws VulcanException, IOException { List semesterList = DataObjectConverter.semestersToSemesterEntities( vulcan.getStudentAndParent().getSemesters(), - daoSession.getDiaryDao().queryBuilder().where( - DiaryDao.Properties.Current.eq(true) - ).unique().getId()); - LogUtils.debug("Register semesters: " + semesterList.size()); + diary.getId() + ); + Timber.d("Register semesters (%s)", semesterList.size()); daoSession.getSemesterDao().insertInTx(semesterList); } @@ -126,15 +158,18 @@ 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); Symbol symbol = daoSession.getSymbolDao().queryBuilder().where( SymbolDao.Properties.UserId.eq(account.getId())).unique(); + School school = daoSession.getSchoolDao().queryBuilder().where( + SchoolDao.Properties.SymbolId.eq(symbol.getId())).unique(); + Student student = daoSession.getStudentDao().queryBuilder().where( - StudentDao.Properties.SymbolId.eq(symbol.getId()), + StudentDao.Properties.SchoolId.eq(school.getId()), StudentDao.Properties.Current.eq(true) ).unique(); @@ -147,9 +182,16 @@ public class AccountSync { account.getEmail(), Scrambler.decrypt(account.getEmail(), account.getPassword()), symbol.getSymbol(), - symbol.getSchoolId(), + school.getRealId(), student.getRealId(), diary.getValue() ); } + + private void clearUserData() { + Database database = daoSession.getDatabase(); + DaoMaster.dropAllTables(database, true); + DaoMaster.createAllTables(database, true); + sharedPref.setCurrentUserId(0); + } } 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 59a6e1a3b..187694189 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 53470882d..db4730921 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 0b70695de..59a71845c 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 163a62340..1d0300d69 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 6a1e4dc1f..05dd14fdb 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 3fe22afff..1ea2fa488 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 000000000..92f5d9a19 --- /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 000000000..d6d4dfa2f --- /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 000000000..74b77d520 --- /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 2a74c32d3..000000000 --- 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 04139d998..000000000 --- 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 919c77a0b..000000000 --- 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 3365a317e..000000000 --- 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 d4bf7bb77..000000000 --- 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 9f56364c4..000000000 --- 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 cdffe73a1..000000000 --- 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 7c2b39849..000000000 --- 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 005b7aa12..000000000 --- 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 f103994a5..c9fc8a5b2 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 fabcefbaa..67a9f8209 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 98f364f76..a4d37c382 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 ab79034f0..9854666ef 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,18 @@ 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; + +import static io.github.wulkanowy.utils.TimeUtilsKt.isHolidays; public class SyncJob extends SimpleJobService { @@ -60,11 +63,17 @@ public class SyncJob extends SimpleJobService { @Override public void onCreate() { super.onCreate(); - ((WulkanowyApp) getApplication()).getApplicationComponent().inject(this); + AndroidInjection.inject(this); } @Override public int onRunJob(JobParameters job) { + if (isHolidays()) { + stop(getApplicationContext()); + + return JobService.RESULT_FAIL_NORETRY; + } + try { repository.getSyncRepo().initLastUser(); repository.getSyncRepo().syncAll(); @@ -74,13 +83,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 +142,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 728cc805d..f442542ef 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 c5c66df0c..82ab9d19f 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 2a4dc5696..3bfa40d13 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 efb9d61a9..e2a5a9e02 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 611b7a29e..8f9b8af0b 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 9d6e8b128..a0960af2c 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 570e1b265..96bb49d6b 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 000000000..0cb26c496 --- /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 3140ad785..d8461bf3b 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 @@ -11,6 +11,7 @@ import io.github.wulkanowy.api.login.BadCredentialsException; import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.ui.base.BasePresenter; import io.github.wulkanowy.utils.AppConstant; +import io.github.wulkanowy.utils.FabricUtils; public class LoginPresenter extends BasePresenter implements LoginContract.Presenter { @@ -46,7 +47,7 @@ public class LoginPresenter extends BasePresenter loginAsync.execute(); } else { - getView().onNoNetworkError(); + getView().showNoNetworkMessage(); } getView().hideSoftInput(); @@ -82,20 +83,30 @@ public class LoginPresenter extends BasePresenter } @Override - public void onEndAsync(boolean success, Exception exception) { - if (success) { - getView().openMainActivity(); - return; - } else if (exception instanceof BadCredentialsException) { - getView().setErrorPassIncorrect(); - getView().showSoftInput(); - } else if (exception instanceof AccountPermissionException) { - getView().setErrorSymbolRequired(); - getView().showSoftInput(); - } else { - 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); } @@ -157,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 b22194da9..2938a836d 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 6b7d07cf3..b739ae98c 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 cc3266548..cb4e29e46 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 000000000..ff3fed67a --- /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 ba5e41d8e..2ca9eefa9 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,11 +1,14 @@ package io.github.wulkanowy.ui.main; +import android.support.annotation.NonNull; import javax.inject.Inject; import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.ui.base.BasePresenter; +import static io.github.wulkanowy.utils.TimeUtilsKt.isHolidays; + public class MainPresenter extends BasePresenter implements MainContract.Presenter { @@ -17,15 +20,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(); } @@ -33,7 +36,7 @@ public class MainPresenter extends BasePresenter getView().initiationBottomNav(tabPosition); getView().initiationViewPager(tabPosition); - if (getRepository().getSharedRepo().isServicesEnable()) { + if (getRepository().getSharedRepo().isServicesEnable() && !isHolidays()) { getView().startSyncService(getRepository().getSharedRepo().getServicesInterval(), getRepository().getSharedRepo().isMobileDisable()); } 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 daee63d28..1ad66787e 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 5e8f3a025..ea38e51d6 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 e26c9fc7b..732806db9 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 000000000..8679cf100 --- /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 55541ed26..73a2837ca 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; @@ -8,10 +10,12 @@ import javax.inject.Inject; import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.ui.base.BasePresenter; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; -import io.github.wulkanowy.utils.TimeUtils; import io.github.wulkanowy.utils.async.AbstractTask; import io.github.wulkanowy.utils.async.AsyncListeners; +import static io.github.wulkanowy.utils.TimeUtilsKt.getFirstDayOfCurrentWeek; +import static io.github.wulkanowy.utils.TimeUtilsKt.getMondaysFromCurrentSchoolYear; + public class AttendancePresenter extends BasePresenter implements AttendanceContract.Presenter, AsyncListeners.OnFirstLoadingListener { @@ -31,8 +35,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()) { @@ -40,11 +44,11 @@ public class AttendancePresenter extends BasePresenter } if (dates.isEmpty()) { - dates = TimeUtils.getMondaysFromCurrentSchoolYear(); + dates = getMondaysFromCurrentSchoolYear(); } if (positionToScroll == 0) { - positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true)); + positionToScroll = dates.indexOf(getFirstDayOfCurrentWeek()); } if (!isFirstSight) { @@ -64,7 +68,7 @@ public class AttendancePresenter extends BasePresenter } @Override - public void onDoInBackgroundLoading() throws Exception { + public void onDoInBackgroundLoading() { for (String date : dates) { getView().setTabDataToAdapter(date); } @@ -92,14 +96,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 b7e6aecd9..86128109b 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 440d878e7..65c8329ef 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 dcb16bf88..a83e67e86 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 e5e65bb9d..8de474690 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 000000000..380a221f2 --- /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 88% 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 47927feb4..7d0c26e5a 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; @@ -10,6 +12,7 @@ import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; import io.github.wulkanowy.data.db.dao.entities.Day; import io.github.wulkanowy.data.db.dao.entities.Week; import io.github.wulkanowy.ui.base.BasePresenter; +import io.github.wulkanowy.utils.FabricUtils; import io.github.wulkanowy.utils.async.AbstractTask; import io.github.wulkanowy.utils.async.AsyncListeners; @@ -21,7 +24,7 @@ public class AttendanceTabPresenter extends BasePresenter headerItems = new ArrayList<>(); + private List headerItems = new ArrayList<>(); private String date; @@ -33,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 cab3b902e..05557b81e 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 000000000..8d56cf234 --- /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 5f6cd4dc9..20c0fe2e1 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; @@ -8,10 +10,12 @@ import javax.inject.Inject; import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.ui.base.BasePresenter; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; -import io.github.wulkanowy.utils.TimeUtils; import io.github.wulkanowy.utils.async.AbstractTask; import io.github.wulkanowy.utils.async.AsyncListeners; +import static io.github.wulkanowy.utils.TimeUtilsKt.getFirstDayOfCurrentWeek; +import static io.github.wulkanowy.utils.TimeUtilsKt.getMondaysFromCurrentSchoolYear; + public class ExamsPresenter extends BasePresenter implements ExamsContract.Presenter, AsyncListeners.OnFirstLoadingListener { @@ -31,8 +35,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()) { @@ -40,11 +44,11 @@ public class ExamsPresenter extends BasePresenter } if (dates.isEmpty()) { - dates = TimeUtils.getMondaysFromCurrentSchoolYear(); + dates = getMondaysFromCurrentSchoolYear(); } if (positionToScroll == 0) { - positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true)); + positionToScroll = dates.indexOf(getFirstDayOfCurrentWeek()); } if (!isFirstSight) { @@ -69,7 +73,7 @@ public class ExamsPresenter extends BasePresenter } @Override - public void onDoInBackgroundLoading() throws Exception { + public void onDoInBackgroundLoading() { for (String date : dates) { getView().setTabDataToAdapter(date); } @@ -91,13 +95,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 86070515b..6c0c364ec 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 ec59628a8..4e0785bb2 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 d41e34d2b..1b6f361a4 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 000000000..fc9a7595e --- /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 86% 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 8c8914a0d..b60a4607b 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; @@ -10,6 +12,7 @@ import io.github.wulkanowy.data.db.dao.entities.Day; import io.github.wulkanowy.data.db.dao.entities.Exam; import io.github.wulkanowy.data.db.dao.entities.Week; import io.github.wulkanowy.ui.base.BasePresenter; +import io.github.wulkanowy.utils.FabricUtils; import io.github.wulkanowy.utils.async.AbstractTask; import io.github.wulkanowy.utils.async.AsyncListeners; @@ -28,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); } @@ -64,7 +67,7 @@ public class ExamsTabPresenter extends BasePresenter refreshTask.setOnRefreshListener(this); refreshTask.execute(); } else { - getView().onNoNetworkError(); + getView().showNoNetworkMessage(); getView().hideRefreshingBar(); } } @@ -90,9 +93,11 @@ public class ExamsTabPresenter extends BasePresenter getView().onRefreshSuccess(); } else { - getView().onError(getRepository().getResRepo().getErrorLoginMessage(exception)); + getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception)); } getView().hideRefreshingBar(); + + FabricUtils.logRefresh("Exams", result, date); } @Override @@ -111,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(); @@ -154,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 a48b340d5..d2423ec8d 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,17 +30,20 @@ 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); + + void onSemesterSwitchActive(); } } 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 b8da5a969..17ebc6e61 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 32c1f3d30..ca1b949c7 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,42 +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) { - 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); @@ -123,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)); @@ -148,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 75% 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 5d0fd0731..03f8564a4 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 @@ -113,34 +113,46 @@ public class GradeHeaderItem averageText.setText(getGradesAverageString()); predictedText.setText(resources.getString(R.string.info_grades_predicted_rating, - item.getPredictedRating())); + GradeUtils.getShortGradeValue(item.getPredictedRating()))); finalText.setText(resources.getString(R.string.info_grades_final_rating, - item.getFinalRating())); + GradeUtils.getShortGradeValue(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 000000000..25eeef484 --- /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 0927d1458..0f4d363cb 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,7 +1,15 @@ package io.github.wulkanowy.ui.main.grades; +import android.support.annotation.NonNull; + +import com.crashlytics.android.answers.Answers; +import com.crashlytics.android.answers.CustomEvent; + +import org.threeten.bp.LocalDate; + import java.util.ArrayList; import java.util.List; +import java.util.Locale; import javax.inject.Inject; @@ -10,6 +18,8 @@ import io.github.wulkanowy.data.db.dao.entities.Grade; 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; @@ -23,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()) { @@ -48,23 +66,23 @@ public class GradesPresenter extends BasePresenter if (!isFirstSight) { isFirstSight = true; - reloadGrades(); } } + @Override + public void onSemesterSwitchActive() { + cancelAsyncTasks(); + } + @Override public void onSemesterChange(int which) { semesterName = which + 1; getView().setCurrentSemester(which); - reloadGrades(); - } - private void reloadGrades() { - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); + Answers.getInstance().logCustom(new CustomEvent("Semester change") + .putCustomAttribute("Name", semesterName)); } @Override @@ -81,7 +99,7 @@ public class GradesPresenter extends BasePresenter refreshTask.setOnRefreshListener(this); refreshTask.execute(); } else { - getView().onNoNetworkError(); + getView().showNoNetworkMessage(); getView().hideRefreshingBar(); } } @@ -100,8 +118,8 @@ public class GradesPresenter extends BasePresenter } @Override - public void onEndRefreshAsync(boolean success, Exception exception) { - if (success) { + public void onEndRefreshAsync(boolean result, Exception exception) { + if (result) { reloadGrades(); int numberOfNewGrades = getRepository().getDbRepo().getNewGrades(semesterName).size(); @@ -112,9 +130,11 @@ public class GradesPresenter extends BasePresenter getView().onRefreshSuccess(numberOfNewGrades); } } else { - getView().onError(getRepository().getResRepo().getErrorLoginMessage(exception)); + getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception)); } getView().hideRefreshingBar(); + + FabricUtils.logRefresh("Grades", result, LocalDate.now().toString()); } @Override @@ -123,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<>(); @@ -142,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 @@ -153,13 +181,36 @@ 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(); } - @Override - public void onDestroy() { - isFirstSight = false; + 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); refreshTask = null; @@ -168,6 +219,12 @@ public class GradesPresenter extends BasePresenter loadingTask.cancel(true); loadingTask = null; } - super.onDestroy(); + } + + @Override + public void detachView() { + isFirstSight = false; + cancelAsyncTasks(); + 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 de9c54c6c..ee9950406 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 @@ -88,6 +88,9 @@ public class GradesSubItem @BindView(R.id.grade_subitem_date) TextView date; + @BindView(R.id.grade_subitem_weight) + TextView weight; + @BindView(R.id.grade_subitem_alert_image) View alert; @@ -110,8 +113,9 @@ public class GradesSubItem value.setText(item.getValue()); value.setBackgroundResource(GradeUtils.getValueColor(item.getValue())); - date.setText(item.getDate()); description.setText(getDescriptionString()); + date.setText(item.getDate()); + weight.setText(String.format("%s: %s", context.getResources().getString(R.string.grade_weight_text), item.getWeight())); alert.setVisibility(item.getRead() ? View.INVISIBLE : View.VISIBLE); if (!item.getRead()) { 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 000000000..a06fa0d55 --- /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 000000000..58f418aa5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummarySubItem.java @@ -0,0 +1,84 @@ +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; +import io.github.wulkanowy.utils.GradeUtils; + +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(GradeUtils.getShortGradeValue(item.getPredictedRating())); + finalGrade.setText(GradeUtils.getShortGradeValue(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 fe677de43..000000000 --- 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 e08a49026..0f2009beb 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,27 @@ 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; + +import static io.github.wulkanowy.utils.TimeUtilsKt.isHolidays; 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 +29,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 +39,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 +83,18 @@ 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", R.string.pref_about_osl)); + + if (isHolidays()) { + Preference services = findPreference(SHARED_KEY_SERVICES_ENABLE); + services.setSummary(R.string.pref_services_suspended_on_holidays); + services.setEnabled(false); + } } @Override @@ -43,21 +102,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 +109,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 +137,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 +172,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 57eca17e4..8344ad767 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 6c5fd501f..db3cc83c4 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 000000000..9ad1ee602 --- /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 d9c9c9c66..1e9cf0b81 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; @@ -8,10 +10,12 @@ import javax.inject.Inject; import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.ui.base.BasePresenter; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; -import io.github.wulkanowy.utils.TimeUtils; import io.github.wulkanowy.utils.async.AbstractTask; import io.github.wulkanowy.utils.async.AsyncListeners; +import static io.github.wulkanowy.utils.TimeUtilsKt.getFirstDayOfCurrentWeek; +import static io.github.wulkanowy.utils.TimeUtilsKt.getMondaysFromCurrentSchoolYear; + public class TimetablePresenter extends BasePresenter implements TimetableContract.Presenter, AsyncListeners.OnFirstLoadingListener { @@ -31,8 +35,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()) { @@ -40,11 +44,11 @@ public class TimetablePresenter extends BasePresenter } if (dates.isEmpty()) { - dates = TimeUtils.getMondaysFromCurrentSchoolYear(); + dates = getMondaysFromCurrentSchoolYear(); } if (positionToScroll == 0) { - positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true)); + positionToScroll = dates.indexOf(getFirstDayOfCurrentWeek()); } if (!isFirstSight) { @@ -64,7 +68,7 @@ public class TimetablePresenter extends BasePresenter } @Override - public void onDoInBackgroundLoading() throws Exception { + public void onDoInBackgroundLoading() { for (String date : dates) { getView().setTabDataToAdapter(date); } @@ -91,13 +95,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 b98636b12..cb877387e 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 a3e84dfa2..2bf47b335 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 0d6bdf2a6..0814ff3c9 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 53d9a67b4..87db51951 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 000000000..1afb055ff --- /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 77% 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 ca582726c..f30dd804e 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,5 +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; @@ -11,9 +14,14 @@ 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.async.AbstractTask; import io.github.wulkanowy.utils.async.AsyncListeners; +import static io.github.wulkanowy.utils.TimeUtilsKt.getParsedDate; +import static io.github.wulkanowy.utils.TimeUtilsKt.isDateInWeek; + public class TimetableTabPresenter extends BasePresenter implements TimetableTabContract.Presenter, AsyncListeners.OnRefreshListener, AsyncListeners.OnFirstLoadingListener { @@ -22,7 +30,7 @@ public class TimetableTabPresenter extends BasePresenter headerItems = new ArrayList<>(); + private List headerItems = new ArrayList<>(); private String date; @@ -36,8 +44,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 000000000..838b411d3 --- /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 8d20226fc..694233481 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 78e80580a..9e6323969 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,33 +13,27 @@ 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; -import io.github.wulkanowy.utils.TimeUtils; + +import static io.github.wulkanowy.utils.TimeUtilsKt.getFirstDayOfCurrentWeek; +import static io.github.wulkanowy.utils.TimeUtilsKt.getTodayOrNextDayOrder; 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,13 +42,12 @@ public class TimetableWidgetFactory implements RemoteViewsService.RemoteViewsFac @Override public void onDataSetChanged() { - inject(); lessonList = new ArrayList<>(); if (repository.getSharedRepo().isUserLoggedIn()) { - Week week = repository.getDbRepo().getWeek(TimeUtils.getDateOfCurrentMonday(true)); - int valueOfDay = TimeUtils.getTodayOrNextDayValue(repository.getSharedRepo().getTimetableWidgetState()); + Week week = repository.getDbRepo().getWeek(getFirstDayOfCurrentWeek()); + int valueOfDay = getTodayOrNextDayOrder(repository.getSharedRepo().getTimetableWidgetState()); if (valueOfDay != 5 && valueOfDay != 6 && week != null) { week.resetDayList(); @@ -84,13 +78,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 d68c7e9a0..aa164e29e 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,12 +11,13 @@ 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; -import io.github.wulkanowy.utils.TimeUtils; + +import static io.github.wulkanowy.utils.TimeUtilsKt.getTodayOrNextDay; public class TimetableWidgetProvider extends AppWidgetProvider { @@ -26,7 +27,6 @@ public class TimetableWidgetProvider extends AppWidgetProvider { RepositoryContract repository; @Override - public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { inject(context); @@ -96,7 +96,7 @@ public class TimetableWidgetProvider extends AppWidgetProvider { : R.string.widget_timetable_today); views.setTextViewText(R.id.timetable_widget_toggle, toggleText); - views.setTextViewText(R.id.timetable_widget_date, TimeUtils.getTodayOrNextDay(nextDay)); + views.setTextViewText(R.id.timetable_widget_date, getTodayOrNextDay(nextDay)); } private void updateWidget(RemoteViews views, AppWidgetManager appWidgetManager, int appWidgetId) { @@ -106,7 +106,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 8ec73faf7..9c7b35fb4 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 3f733ad6d..cd4490d60 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java +++ b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java @@ -9,6 +9,7 @@ import io.github.wulkanowy.data.db.dao.entities.Day; import io.github.wulkanowy.data.db.dao.entities.Diary; import io.github.wulkanowy.data.db.dao.entities.Exam; import io.github.wulkanowy.data.db.dao.entities.Grade; +import io.github.wulkanowy.data.db.dao.entities.School; import io.github.wulkanowy.data.db.dao.entities.Semester; import io.github.wulkanowy.data.db.dao.entities.Student; import io.github.wulkanowy.data.db.dao.entities.Subject; @@ -21,14 +22,14 @@ public final class DataObjectConverter { throw new IllegalStateException("Utility class"); } - public static List studentsToStudentEntities(List students, Long symbolId) { - List studentList = new ArrayList<>(); + public static List schoolsToSchoolsEntities(List schools, Long symbolId) { + List studentList = new ArrayList<>(); - for (io.github.wulkanowy.api.Student student : students) { - studentList.add(new Student() - .setName(student.getName()) - .setCurrent(student.isCurrent()) - .setRealId(student.getId()) + for (io.github.wulkanowy.api.generic.School school : schools) { + studentList.add(new School() + .setName(school.getName()) + .setCurrent(school.getCurrent()) + .setRealId(school.getId()) .setSymbolId(symbolId) ); } @@ -36,10 +37,25 @@ public final class DataObjectConverter { return studentList; } - public static List diariesToDiaryEntities(List diaryList, Long studentId) { + public static List studentsToStudentEntities(List students, Long schoolId) { + List studentList = new ArrayList<>(); + + for (io.github.wulkanowy.api.generic.Student student : students) { + studentList.add(new Student() + .setName(student.getName()) + .setCurrent(student.isCurrent()) + .setRealId(student.getId()) + .setSchoolId(schoolId) + ); + } + + return studentList; + } + + 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 +66,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 new file mode 100644 index 000000000..69897caa0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/FabricUtils.java @@ -0,0 +1,37 @@ +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 { + + private FabricUtils() { + throw new IllegalStateException("Utility class"); + } + + 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("message", message) + ); + } + + public static void logRefresh(String name, boolean result, String date) { + Answers.getInstance().logCustom( + new CustomEvent(name + " refresh") + .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 32bc5cd08..7b6ba193a 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,81 +17,156 @@ 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; + } + + public static float calculateSubjectsAverage(List subjectList, boolean usePredicted) { + return calculateSubjectsAverage(subjectList, usePredicted, false); + } + + public static float calculateDetailedSubjectsAverage(List subjectList) { + return calculateSubjectsAverage(subjectList, false, true); + } + + public static int getValueColor(String value) { + Matcher m1 = validGradePattern.matcher(value); + if (!m1.find()) { + return R.color.grade_default; + } + + Matcher m2 = simpleGradeValuePattern.matcher(m1.group()); + if (!m2.find()) { + return R.color.grade_default; + } + + switch (Integer.parseInt(m2.group())) { + case 6: + return R.color.grade_six; + case 5: + return R.color.grade_five; + case 4: + return R.color.grade_four; + case 3: + return R.color.grade_three; + case 2: + return R.color.grade_two; + case 1: + return R.color.grade_one; + default: + return R.color.grade_default; } } - 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("[-]", ""); + 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; + } + + public 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; + } + } + + public static String getShortGradeValue(String grade) { + switch (grade) { + case "celujący": + return "6"; + case "bardzo dobry": + return "5"; + case "dobry": + return "4"; + case "dostateczny": + return "3"; + case "dopuszczający": + return "2"; + case "niedostateczny": + return "1"; + default: + return grade; + } + } + + 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 (valueOfGrade.matches("[+][0-6]") - || valueOfGrade.matches("[0-6][+]")) { - String replacedValue = valueOfGrade.replaceAll("[+]", ""); + } else if (value.matches("[+][0-6]") || value.matches("[0-6][+]")) { + String replacedValue = value.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}", ""); + } 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(valueOfGrade); + return Float.valueOf(value); } } else { return -1; } } - private static int getIntegerForWeightOfGrade(String weightOfGrade) { + private static int getWeightValue(String weightOfGrade) { return Integer.valueOf(weightOfGrade.substring(0, weightOfGrade.length() - 3)); } - - public static int getValueColor(String value) { - Matcher m1 = validGradePattern.matcher(value); - if (!m1.find()) { - return R.color.default_grade; - } - - Matcher m2 = simpleGradeValuePattern.matcher(m1.group()); - if (!m2.find()) { - return R.color.default_grade; - } - - switch (Integer.parseInt(m2.group())) { - case 6: - return R.color.six_grade; - case 5: - return R.color.five_grade; - case 4: - return R.color.four_grade; - case 3: - return R.color.three_grade; - case 2: - return R.color.two_grade; - case 1: - return R.color.one_grade; - default: - return R.color.default_grade; - } - } } 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 f59bbf641..000000000 --- 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 000000000..6e6c701b1 --- /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 deleted file mode 100644 index de5d136f4..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.java +++ /dev/null @@ -1,105 +0,0 @@ -package io.github.wulkanowy.utils; - -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 List getMondaysFromCurrentSchoolYear() { - LocalDate startDate = LocalDate.of(getCurrentSchoolYear(), 9, 1); - LocalDate endDate = LocalDate.of(getCurrentSchoolYear() + 1, 8, 31); - - List dateList = new ArrayList<>(); - - LocalDate thisMonday = startDate.with(DayOfWeek.MONDAY); - - if (startDate.isAfter(thisMonday)) { - startDate = thisMonday.plusWeeks(1); - } else { - startDate = thisMonday; - } - - while (startDate.isBefore(endDate)) { - dateList.add(startDate.format(formatter)); - startDate = startDate.plusWeeks(1); - } - return dateList; - } - - public static int getCurrentSchoolYear() { - LocalDate localDate = LocalDate.now(); - return localDate.getMonthValue() <= 8 ? localDate.getYear() - 1 : localDate.getYear(); - } - - public static String getDateOfCurrentMonday(boolean normalize) { - LocalDate currentDate = LocalDate.now(); - - if (currentDate.getDayOfWeek() == DayOfWeek.SATURDAY && normalize) { - currentDate = currentDate.plusDays(2); - } else if (currentDate.getDayOfWeek() == DayOfWeek.SUNDAY && normalize) { - currentDate = currentDate.plusDays(1); - } else { - currentDate = currentDate.with(DayOfWeek.MONDAY); - } - return currentDate.format(formatter); - } - - public static int getTodayOrNextDayValue(boolean nextDay) { - DayOfWeek day = LocalDate.now().getDayOfWeek(); - if (nextDay) { - if (day == DayOfWeek.SUNDAY) { - return 0; - } - return day.getValue(); - } - return day.getValue() - 1; - } - - public static String getTodayOrNextDay(boolean nextDay) { - LocalDate current = LocalDate.now(); - return nextDay ? current.plusDays(1).format(formatter) : current.format(formatter); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt new file mode 100644 index 000000000..c61c7e0dc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt @@ -0,0 +1,97 @@ +package io.github.wulkanowy.utils + +import org.threeten.bp.DayOfWeek.* +import org.threeten.bp.LocalDate +import org.threeten.bp.Year +import org.threeten.bp.format.DateTimeFormatter +import org.threeten.bp.temporal.TemporalAdjusters +import java.util.* + +private val formatter = DateTimeFormatter.ofPattern(AppConstant.DATE_PATTERN) + +fun getParsedDate(dateString: String, dateFormat: String): LocalDate { + return LocalDate.parse(dateString, DateTimeFormatter.ofPattern(dateFormat)) +} + +fun getMondaysFromCurrentSchoolYear() = getMondaysFromCurrentSchoolYear(LocalDate.now()) + +fun getMondaysFromCurrentSchoolYear(date: LocalDate): List { + val startDate = getFirstSchoolDay(getSchoolYearForDate(date)) + ?.with(TemporalAdjusters.previousOrSame(MONDAY)) + val endDate = getFirstSchoolDay(getSchoolYearForDate(date) + 1) + ?.with(TemporalAdjusters.previousOrSame(MONDAY)) + + val dateList = ArrayList() + var monday = startDate as LocalDate + while (monday.isBefore(endDate)) { + dateList.add(monday.format(formatter)) + monday = monday.plusWeeks(1) + } + + return dateList +} + +fun getSchoolYearForDate(date: LocalDate): Int { + return if (date.monthValue <= 8) date.year - 1 else date.year +} + +fun getFirstDayOfCurrentWeek(): String = getFirstDayOfCurrentWeek(LocalDate.now()) + +fun getFirstDayOfCurrentWeek(date: LocalDate): String { + return when (date.dayOfWeek) { + SATURDAY -> date.plusDays(2) + SUNDAY -> date.plusDays(1) + else -> date.with(MONDAY) + }.format(formatter) +} + +fun getTodayOrNextDayOrder(next: Boolean): Int = getTodayOrNextDayOrder(next, LocalDate.now()) + +fun getTodayOrNextDayOrder(next: Boolean, date: LocalDate): Int { + val day = date.dayOfWeek + return if (next) { + if (day == SUNDAY) { + 0 + } else day.value + } else day.value - 1 +} + +fun getTodayOrNextDay(next: Boolean): String? = getTodayOrNextDay(next, LocalDate.now()) + +fun getTodayOrNextDay(next: Boolean, date: LocalDate): String? { + return (if (next) { + date.plusDays(1) + } else date).format(formatter) +} + +fun isDateInWeek(firstWeekDay: LocalDate, date: LocalDate): Boolean { + return date.isAfter(firstWeekDay.minusDays(1)) && date.isBefore(firstWeekDay.plusDays(5)) +} + +/** + * [Dz.U. 2016 poz. 1335](http://prawo.sejm.gov.pl/isap.nsf/DocDetails.xsp?id=WDU20160001335) + */ +fun isHolidays(): Boolean = isHolidays(LocalDate.now(), Year.now().value) + +fun isHolidays(day: LocalDate, year: Int): Boolean { + return day.isAfter(getLastSchoolDay(year)) && day.isBefore(getFirstSchoolDay(year)) +} + +fun getFirstSchoolDay(year: Int): LocalDate? { + val firstSeptember = LocalDate.of(year, 9, 1) + + return when (firstSeptember.dayOfWeek) { + FRIDAY, + SATURDAY, + SUNDAY -> firstSeptember.with(TemporalAdjusters.firstInMonth(MONDAY)) + else -> { + firstSeptember + } + } +} + +fun getLastSchoolDay(year: Int): LocalDate? { + return LocalDate + .of(year, 6, 20) + .with(TemporalAdjusters.next(FRIDAY)) +} 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 5106d6417..447214164 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 dc0c409a4..8e425a894 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/listing/title b/app/src/main/play/pl-PL/listing/title index e5160837d..e491efb90 100644 --- a/app/src/main/play/pl-PL/listing/title +++ b/app/src/main/play/pl-PL/listing/title @@ -1 +1 @@ -Wulkanowy Dziennik UONET+ +Wulkanowy Dzienniczek UONET+ diff --git a/app/src/main/play/pl-PL/whatsnew b/app/src/main/play/pl-PL/whatsnew index 8bce40fa1..205817c54 100644 --- a/app/src/main/play/pl-PL/whatsnew +++ b/app/src/main/play/pl-PL/whatsnew @@ -1,4 +1,2 @@ -Wersja 0.4.3: -- naprawiono błąd z pokazywaniem usuniętych lekcji w planie -- naprawiono drobne błędy z wyrównaniem tekstu na starszych urządzeniach -- poprawiono synchronizację w tle spowodowaną błędem w komunikacji z serwerem +Wersja 0.5.4: +- naprawiono błąd 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 000000000..ca44eb29e --- /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 000000000..b7acb9df9 --- /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 000000000..1b2a93b8c --- /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 dc33b8aa2..f46b3ad71 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 4d3c91cfb..149504585 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 fdef73225..c0df16d60 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 662945984..13179b7c3 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 61506dada..3866cd3c7 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 9fed586b0..a07747245 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 a689dc064..63880977b 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 8f3a86faa..000000000 --- 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_login.xml b/app/src/main/res/layout/activity_login.xml index ec3872da9..4a6f08b22 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -61,13 +61,13 @@ android:id="@+id/login_activity_email_text_input" android:layout_width="match_parent" android:layout_height="wrap_content" + android:hint="@string/prompt_email" android:layout_marginBottom="12dp"> @@ -76,6 +76,7 @@ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f9201ad46..3f8473d8c 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 916c993ca..4b0f1ff9a 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 61dac6310..350b24522 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 47fd7bb70..72d4b8ddb 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 3895ae2e2..7f7bf65f3 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 52386de44..3f50f07c4 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 f98e72804..4dc450b58 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 e446b2fbe..433f5b1cb 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 f00b3108c..1f177a600 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 47aa51013..612b5df81 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 97% rename from app/src/main/res/layout/grade_dialog.xml rename to app/src/main/res/layout/grades_dialog.xml index 048f1e1b2..932c895ea 100644 --- a/app/src/main/res/layout/grade_dialog.xml +++ b/app/src/main/res/layout/grades_dialog.xml @@ -29,6 +29,8 @@ android:layout_gravity="end" android:gravity="center" android:text="@string/app_name" + android:textColor="@color/grade_text" + android:background="@color/grade_default" android:textSize="30sp" /> 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 032ad6755..b819dd54c 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" /> @@ -56,7 +59,19 @@ android:layout_alignBottom="@+id/grade_subitem_value" android:layout_alignLeft="@+id/grade_subitem_description" android:layout_alignStart="@+id/grade_subitem_description" - android:text="@string/grades_text" + android:text="@string/dialog_date_text" + android:textSize="12sp" /> + + - \ No newline at end of file + diff --git a/app/src/main/res/layout/grades_summary_header.xml b/app/src/main/res/layout/grades_summary_header.xml new file mode 100644 index 000000000..06954bd62 --- /dev/null +++ b/app/src/main/res/layout/grades_summary_header.xml @@ -0,0 +1,34 @@ + + + + + + + + \ 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 000000000..c7715092c --- /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 017004ec6..5898bc4a9 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 1e8ecec84..53ea8ce45 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 86b173b47..3c3cdc42a 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 c2f146ae3..acf8d238c 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 000000000..0533ae39e --- /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 6f8a027e3..000000000 --- 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 000000000..d0b4f53f9 --- /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 4e66a3730..3731be408 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 aeb828d1a..55eb2865c 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -46,7 +46,7 @@ W obawie o bezpieczeństwo przechowywanych danych osobistych na urządzeniu z podwyższonymi uprawnieniami (root), aplikacja Wulkanowy została zablokowana Opis - Waga + Waga Brak opisu Nauczyciel Data @@ -81,6 +81,9 @@ Brak lekcji w tym tygodniu Sala %s + Przewidywana + Końcowa + %d ocena %d oceny @@ -126,12 +129,14 @@ Pokazuj podsumowanie w ocenach Pokazuj obecność we frekwencji Wymagany restart + Ciemny motyw (Beta) Powiadomienia Pokazuj powiadomienia Usługi Włącz odświeżanie danych w tle + Zawieszone na wakacjach Interwał między odświeżaniem danych Synchronizacja tylko przez WiFi @@ -154,4 +159,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 000000000..3593e29e6 --- /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 90f8c15e9..61685de65 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -4,14 +4,19 @@ #9a0007 #ff6659 #f0f0f0 - #92b53d - #66B266 - #55BBDB - #FFE68C - #CE9AD2 - #d32f2f + + #ffffff + #91b43c + #2cbd92 + #50b6d6 + #d2ab24 + #9071b3 + #d65757 + #3d5f9c + #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 346b57c32..59f4fffed 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 1ffea541e..e2a06e972 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -46,7 +46,7 @@ For the sake of safety of personal data stored on a device with increased authorization (root), the Wulkanowy application has been blocked. Description - Weight + Weight No description Teacher Date @@ -80,6 +80,9 @@ Final: %1$s No lesson in this week + Predicted + Final + Room %s @@ -122,11 +125,14 @@ Show present in attendance Required restart + Dark theme (Beta) + Notifications Show the notifications Services Enable background data refreshing + Suspended on holidays Interval between data refreshing Synchronization via WiFi only @@ -149,4 +155,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 d72afb53c..1eb55f9d0 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,6 +1,6 @@ - -