1
0

Compare commits

...

31 Commits
0.4.2 ... 0.5.0

Author SHA1 Message Date
dab1bd4ac6 Version 0.5.0 2018-06-14 14:45:32 +02:00
11578aa735 Add dark theme (#133) 2018-06-14 11:40:46 +02:00
b63e28f9a9 Separate synchronization from login (#137) 2018-06-13 18:53:42 +02:00
072c504d2b Clean data user on bad user credentials (#138) 2018-06-13 03:07:34 +02:00
7b7be1eef1 API fixes (#136) 2018-06-10 19:46:34 +02:00
81d177c270 Hide actionbar on scroll (#135) 2018-06-09 00:59:39 +02:00
8d014ab7e9 Fix displaying the replacements in widget (#134) 2018-06-07 08:13:49 +02:00
a06d114127 Add logger (#131) 2018-06-06 19:38:54 +02:00
0e16519baf Add a summary of grades (#127) 2018-06-04 21:47:46 +02:00
dde5775a41 Move ticks converter to api (#130) 2018-06-01 12:52:03 +02:00
e2003e2538 Expand current day on startup (#129) 2018-05-31 23:54:59 +02:00
228f680e5d Hide empty fields in summary (#128) 2018-05-31 23:01:52 +02:00
306092ce45 Add mobile access API (#126) 2018-05-30 11:57:29 +02:00
d4b172e022 Version 0.4.5 2018-05-28 11:32:39 +02:00
b4c765b482 [API] Fix timetable endpoint (#125) 2018-05-28 11:29:43 +02:00
3f1fff6d96 Implementation of a new Dagger injection (#103) 2018-05-26 20:44:06 +02:00
b59008a90f Version 0.4.4 2018-05-25 20:33:45 +02:00
74c0dda999 Fix lesson description from warning (#124) 2018-05-25 20:14:16 +02:00
2288ceffb8 Stop refreshing while semester switching window open (#123) 2018-05-25 19:10:34 +02:00
ffe8511e3f Add some fabric answers (#120) 2018-05-25 16:35:12 +02:00
34205e4a8b Fix autofill on api lvl 26 (#122) 2018-05-25 11:49:53 +02:00
ef648c7f8b Clear db before register (#121) 2018-05-24 22:25:24 +02:00
3592946e6f Fix api login(#119) 2018-05-23 19:21:35 +02:00
859f8dc319 Avoid null if cell second div not contain span (#118) 2018-05-23 18:44:04 +02:00
9c97962e89 Version 0.4.3 2018-05-19 23:37:32 +02:00
a4445a8a97 Fix api login error (#115) 2018-05-19 23:02:54 +02:00
3f54d13b6b Add condition to exam query (#116) 2018-05-19 21:11:04 +02:00
5685e73e46 Fix text alignment <= lv l20 (#117) 2018-05-19 19:58:33 +02:00
e9b357e92d Remove the root check (#114) 2018-05-19 12:48:12 +02:00
62bc00cd68 Delete non-existing lessons on sync (#112) 2018-05-19 12:30:17 +02:00
54e6aee82e Stop SyncJob if user is not registered in app (#113) 2018-05-16 20:43:29 +02:00
199 changed files with 2971 additions and 1856 deletions

View File

@ -6,7 +6,7 @@
[![BCH compliance](https://bettercodehub.com/edge/badge/wulkanowy/wulkanowy?branch=master)](https://bettercodehub.com/) [![BCH compliance](https://bettercodehub.com/edge/badge/wulkanowy/wulkanowy?branch=master)](https://bettercodehub.com/)
[![Scrutinizer](https://img.shields.io/scrutinizer/g/wulkanowy/wulkanowy.svg?style=flat-square)](https://scrutinizer-ci.com/g/wulkanowy/wulkanowy/?branch=master) [![Scrutinizer](https://img.shields.io/scrutinizer/g/wulkanowy/wulkanowy.svg?style=flat-square)](https://scrutinizer-ci.com/g/wulkanowy/wulkanowy/?branch=master)
[![Bintray](https://img.shields.io/bintray/v/wulkanowy/wulkanowy/api.svg?style=flat-square)](https://bintray.com/wulkanowy/wulkanowy/api) [![Bintray](https://img.shields.io/bintray/v/wulkanowy/wulkanowy/api.svg?style=flat-square)](https://bintray.com/wulkanowy/wulkanowy/api)
[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/JMG2rhJ) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr)
[Pobierz wersję beta](https://play.google.com/store/apps/details?id=io.github.wulkanowy&amp;utm_source=vcs) [Pobierz wersję beta](https://play.google.com/store/apps/details?id=io.github.wulkanowy&amp;utm_source=vcs)

View File

@ -1,4 +1,5 @@
apply plugin: 'java-library' apply plugin: 'java-library'
apply plugin: 'kotlin'
apply plugin: 'org.sonarqube' apply plugin: 'org.sonarqube'
apply plugin: 'jacoco' apply plugin: 'jacoco'
apply plugin: 'com.jfrog.bintray' apply plugin: 'com.jfrog.bintray'
@ -31,6 +32,9 @@ dependencies {
implementation "org.jsoup:jsoup:$jsoup" implementation "org.jsoup:jsoup:$jsoup"
implementation "org.apache.commons:commons-lang3:$apacheLang" implementation "org.apache.commons:commons-lang3:$apacheLang"
implementation "com.google.code.gson:gson:$gson" 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 "junit:junit:$junit"
testImplementation "org.mockito:mockito-core:$mockito" testImplementation "org.mockito:mockito-core:$mockito"
@ -115,3 +119,29 @@ artifacts {
archives sourcesJar archives sourcesJar
archives javadocJar archives javadocJar
} }
buildscript {
ext.kotlin_version = '1.2.41'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
repositories {
mavenCentral()
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}

View File

@ -3,6 +3,8 @@ package io.github.wulkanowy.api;
import org.jsoup.Connection; import org.jsoup.Connection;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.util.Date; import java.util.Date;
@ -27,6 +29,8 @@ public class Client {
private Cookies cookies = new Cookies(); private Cookies cookies = new Cookies();
private static final Logger logger = LoggerFactory.getLogger(Client.class);
Client(String email, String password, String symbol) { Client(String email, String password, String symbol) {
this.email = email; this.email = email;
this.password = password; this.password = password;
@ -58,12 +62,17 @@ public class Client {
return; return;
} }
clearCookies();
this.symbol = new Login(this).login(email, password, symbol); this.symbol = new Login(this).login(email, password, symbol);
logger.info("Login successful on {} at {}", getHost(), new Date());
} }
private boolean isLoggedIn() { private boolean isLoggedIn() {
logger.debug("Last success request: {}", lastSuccessRequest);
logger.debug("Cookies: {}", getCookies().size());
return getCookies().size() > 0 && lastSuccessRequest != null && return getCookies().size() > 0 && lastSuccessRequest != null &&
29 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime()); 5 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime());
} }
@ -75,14 +84,14 @@ public class Client {
this.symbol = symbol; this.symbol = symbol;
} }
public void addCookies(Map<String, String> items) {
cookies.addItems(items);
}
private Map<String, String> getCookies() { private Map<String, String> getCookies() {
return cookies.getItems(); return cookies.getItems();
} }
public void clearCookies() {
cookies = new Cookies();
}
String getHost() { String getHost() {
return host; return host;
} }
@ -111,14 +120,18 @@ public class Client {
this.cookies.addItems(cookies); 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) .followRedirects(true)
.cookies(getCookies()) .cookies(getCookies())
.execute(); .execute();
this.cookies.addItems(response.cookies()); this.cookies.addItems(response.cookies());
Document doc = checkForErrors(response.parse()); Document doc = checkForErrors(response.parse(), response.statusCode());
if (loginBefore) { if (loginBefore) {
lastSuccessRequest = new Date(); lastSuccessRequest = new Date();
@ -128,7 +141,11 @@ public class Client {
} }
public synchronized Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException { 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) { for (String[] data : params) {
connection.data(data[0], data[1]); connection.data(data[0], data[1]);
@ -142,15 +159,19 @@ public class Client {
this.cookies.addItems(response.cookies()); this.cookies.addItems(response.cookies());
response.bufferUp(); // fixes cert parsing issues 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 { public String getJsonStringByUrl(String url) throws IOException, VulcanException {
login(); login();
Connection.Response response = Jsoup.connect(getFilledUrl(url)) url = getFilledUrl(url);
logger.debug("GET {}", url);
Connection.Response response = Jsoup.connect(url)
.followRedirects(true) .followRedirects(true)
.ignoreContentType(true) .ignoreContentType(true)
.cookies(getCookies()) .cookies(getCookies())
@ -164,7 +185,11 @@ public class Client {
public String postJsonStringByUrl(String url, String[][] params) throws IOException, VulcanException { public String postJsonStringByUrl(String url, String[][] params) throws IOException, VulcanException {
login(); login();
Connection connection = Jsoup.connect(getFilledUrl(url)); url = getFilledUrl(url);
logger.debug("POST {}", url);
Connection connection = Jsoup.connect(url);
for (String[] data : params) { for (String[] data : params) {
connection.data(data[0], data[1]); connection.data(data[0], data[1]);
@ -182,7 +207,7 @@ public class Client {
return response.body(); return response.body();
} }
Document checkForErrors(Document doc) throws VulcanException { Document checkForErrors(Document doc, int code) throws VulcanException {
lastSuccessRequest = null; lastSuccessRequest = null;
String title = doc.select("title").text(); String title = doc.select("title").text();
@ -196,7 +221,7 @@ public class Client {
} }
if ("Błąd strony".equals(title)) { if ("Błąd strony".equals(title)) {
throw new VulcanException("Nieznany błąd"); throw new NotLoggedInErrorException(title + " " + doc.body() + ", status: " + code);
} }
return doc; return doc;

View File

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

View File

@ -6,6 +6,11 @@ import org.jsoup.nodes.Element;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import io.github.wulkanowy.api.generic.Diary;
import io.github.wulkanowy.api.generic.ParamItem;
import io.github.wulkanowy.api.generic.Semester;
import io.github.wulkanowy.api.generic.Student;
public interface SnP { public interface SnP {
String getSchoolID(); String getSchoolID();

View File

@ -3,6 +3,8 @@ package io.github.wulkanowy.api;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
@ -11,6 +13,11 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import io.github.wulkanowy.api.generic.Diary;
import io.github.wulkanowy.api.generic.ParamItem;
import io.github.wulkanowy.api.generic.Semester;
import io.github.wulkanowy.api.generic.Student;
public class StudentAndParent implements SnP { public class StudentAndParent implements SnP {
private static final String START_PAGE_URL = "{schema}://uonetplus.{host}/{symbol}/Start.mvc/Index"; private static final String START_PAGE_URL = "{schema}://uonetplus.{host}/{symbol}/Start.mvc/Index";
@ -27,6 +34,8 @@ public class StudentAndParent implements SnP {
private String diaryID; private String diaryID;
private static final Logger logger = LoggerFactory.getLogger(StudentAndParent.class);
StudentAndParent(Client client, String schoolID, String studentID, String diaryID) { StudentAndParent(Client client, String schoolID, String studentID, String diaryID) {
this.client = client; this.client = client;
this.schoolID = schoolID; this.schoolID = schoolID;
@ -38,6 +47,11 @@ public class StudentAndParent implements SnP {
if (null == getStudentID() || "".equals(getStudentID())) { if (null == getStudentID() || "".equals(getStudentID())) {
Document doc = client.getPageByUrl(getSnpHomePageUrl()); Document doc = client.getPageByUrl(getSnpHomePageUrl());
if (doc.select("#idSection").isEmpty()) {
logger.error("Expected SnP page, got page with title: {} {}", doc.title(), doc.selectFirst("body"));
throw new VulcanException("Nieznany błąd podczas pobierania danych. Strona: " + doc.title());
}
Student student = getCurrent(getStudents(doc)); Student student = getCurrent(getStudents(doc));
studentID = student.getId(); studentID = student.getId();
@ -65,26 +79,29 @@ public class StudentAndParent implements SnP {
return getBaseUrl(); return getBaseUrl();
} }
// get url to uonetplus-opiekun.vulcan.net.pl // get url to uonetplus-opiekun.fakelog.cf
Document startPage = client.getPageByUrl(START_PAGE_URL); Document startPage = client.getPageByUrl(START_PAGE_URL);
Element studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a").first(); Elements studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a");
if (null == studentTileLink) { logger.debug("studentTileLink: {}", studentTileLink.size());
throw new NotLoggedInErrorException("You are probably not logged in. Force login");
if (studentTileLink.isEmpty()) {
throw new VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?");
} }
String snpPageUrl = studentTileLink.attr("href"); String snpPageUrl = studentTileLink.last().attr("href");
this.schoolID = getExtractedIdFromUrl(snpPageUrl); this.schoolID = getExtractedIdFromUrl(snpPageUrl);
return snpPageUrl; return snpPageUrl;
} }
String getExtractedIdFromUrl(String snpPageUrl) throws NotLoggedInErrorException { String getExtractedIdFromUrl(String snpPageUrl) throws VulcanException {
String[] path = snpPageUrl.split(client.getHost())[1].split("/"); String[] path = snpPageUrl.split(client.getHost())[1].split("/");
if (5 != path.length) { if (5 != path.length) {
throw new NotLoggedInErrorException("You are probably not logged in"); logger.error("Expected snp url, got {}", snpPageUrl);
throw new VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?");
} }
return path[2]; return path[2];
@ -102,12 +119,12 @@ public class StudentAndParent implements SnP {
Map<String, String> cookies = new HashMap<>(); Map<String, String> cookies = new HashMap<>();
cookies.put("idBiezacyDziennik", diaryID); cookies.put("idBiezacyDziennik", diaryID);
cookies.put("idBiezacyUczen", studentID); cookies.put("idBiezacyUczen", studentID);
client.addCookies(cookies);
Document doc = client.getPageByUrl(getBaseUrl() + url, true, cookies); Document doc = client.getPageByUrl(getBaseUrl() + url, true, cookies);
if (!doc.title().startsWith("Witryna ucznia i rodzica")) { 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")) { if (doc.title().endsWith("Strona główna")) {

View File

@ -1,5 +1,8 @@
package io.github.wulkanowy.api; package io.github.wulkanowy.api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import io.github.wulkanowy.api.attendance.AttendanceStatistics; import io.github.wulkanowy.api.attendance.AttendanceStatistics;
@ -8,6 +11,8 @@ import io.github.wulkanowy.api.exams.ExamsWeek;
import io.github.wulkanowy.api.grades.GradesList; import io.github.wulkanowy.api.grades.GradesList;
import io.github.wulkanowy.api.grades.SubjectsList; import io.github.wulkanowy.api.grades.SubjectsList;
import io.github.wulkanowy.api.messages.Messages; 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.AchievementsList;
import io.github.wulkanowy.api.notes.NotesList; import io.github.wulkanowy.api.notes.NotesList;
import io.github.wulkanowy.api.school.SchoolInfo; import io.github.wulkanowy.api.school.SchoolInfo;
@ -28,17 +33,21 @@ public class Vulcan {
private String diaryId; 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) { public void setCredentials(String email, String password, String symbol, String schoolId, String studentId, String diaryId) {
this.schoolId = schoolId; this.schoolId = schoolId;
this.studentId = studentId; this.studentId = studentId;
this.diaryId = diaryId; this.diaryId = diaryId;
client = new Client(email, password, symbol); client = new Client(email, password, symbol);
logger.debug("Client created with symbol " + symbol);
} }
public Client getClient() throws NotLoggedInErrorException { public Client getClient() throws NotLoggedInErrorException {
if (null == client) { 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; return client;
@ -108,6 +117,14 @@ public class Vulcan {
return new FamilyInformation(getStudentAndParent()); 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 { public Messages getMessages() throws VulcanException {
return new Messages(getClient()); return new Messages(getClient());
} }

View File

@ -4,12 +4,8 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
import io.github.wulkanowy.api.SnP; import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException; 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.Lesson;
import io.github.wulkanowy.api.generic.Week; 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 { public class AttendanceTable {
private final static String ATTENDANCE_PAGE_URL = "Frekwencja.mvc?data="; private final static String ATTENDANCE_PAGE_URL = "Frekwencja.mvc?data=";
@ -27,13 +26,12 @@ public class AttendanceTable {
this.snp = snp; this.snp = snp;
} }
public Week<Day> getWeekTable() throws IOException, ParseException, VulcanException { public Week<Day> getWeekTable() throws IOException, VulcanException {
return getWeekTable(""); return getWeekTable("");
} }
public Week<Day> getWeekTable(String tick) throws IOException, ParseException, VulcanException { public Week<Day> getWeekTable(String date) throws IOException, VulcanException {
Element table = snp.getSnPPageDocument(ATTENDANCE_PAGE_URL + tick) Element table = snp.getSnPPageDocument(ATTENDANCE_PAGE_URL + getDateAsTick(date))
.select(".mainContainer .presentData").first(); .select(".mainContainer .presentData").first();
Elements headerCells = table.select("thead th"); Elements headerCells = table.select("thead th");
@ -42,14 +40,10 @@ public class AttendanceTable {
for (int i = 1; i < headerCells.size(); i++) { for (int i = 1; i < headerCells.size(); i++) {
String[] dayHeaderCell = headerCells.get(i).html().split("<br>"); String[] dayHeaderCell = headerCells.get(i).html().split("<br>");
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT); days.add(new Day()
Date d = sdf.parse(dayHeaderCell[1].trim()); .setDayName(dayHeaderCell[0])
sdf.applyPattern("yyyy-MM-dd"); .setDate(getFormattedDate(dayHeaderCell[1].trim()))
);
Day day = new Day();
day.setDayName(dayHeaderCell[0]);
day.setDate(sdf.format(d));
days.add(day);
} }
Elements hoursInDays = table.select("tbody tr"); Elements hoursInDays = table.select("tbody tr");
@ -63,7 +57,7 @@ public class AttendanceTable {
for (int i = 1; i < size; i++) { for (int i = 1; i < size; i++) {
Lesson lesson = new Lesson(); Lesson lesson = new Lesson();
lesson.setDate(days.get(i - 1).getDate()); lesson.setDate(days.get(i - 1).getDate());
lesson.setNumber(hours.get(0).text()); lesson.setNumber(Integer.valueOf(hours.get(0).text()));
addLessonDetails(lesson, hours.get(i)); addLessonDetails(lesson, hours.get(i));

View File

@ -6,17 +6,16 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
import io.github.wulkanowy.api.SnP; import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException; import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.api.generic.Week; 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 { public class ExamsWeek {
private static final String EXAMS_PAGE_URL = "Sprawdziany.mvc/Terminarz?rodzajWidoku=2&data="; private static final String EXAMS_PAGE_URL = "Sprawdziany.mvc/Terminarz?rodzajWidoku=2&data=";
@ -27,12 +26,12 @@ public class ExamsWeek {
this.snp = snp; this.snp = snp;
} }
public Week<ExamDay> getCurrent() throws IOException, VulcanException, ParseException { public Week<ExamDay> getCurrent() throws IOException, VulcanException {
return getWeek("", true); return getWeek("", true);
} }
public Week<ExamDay> getWeek(String tick, final boolean onlyNotEmpty) throws IOException, VulcanException, ParseException { public Week<ExamDay> getWeek(String date, final boolean onlyNotEmpty) throws IOException, VulcanException {
Document examsPage = snp.getSnPPageDocument(EXAMS_PAGE_URL + tick); Document examsPage = snp.getSnPPageDocument(EXAMS_PAGE_URL + getDateAsTick(date));
Elements examsDays = examsPage.select(".mainContainer > div:not(.navigation)"); Elements examsDays = examsPage.select(".mainContainer > div:not(.navigation)");
List<ExamDay> days = new ArrayList<>(); List<ExamDay> days = new ArrayList<>();
@ -71,11 +70,4 @@ public class ExamsWeek {
.first().text().split(" ")[1])) .first().text().split(" ")[1]))
.setDays(days); .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);
}
} }

View File

@ -37,7 +37,8 @@ public class Day {
return dayName; return dayName;
} }
public void setDayName(String dayName) { public Day setDayName(String dayName) {
this.dayName = dayName; this.dayName = dayName;
return this;
} }
} }

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.api; package io.github.wulkanowy.api.generic;
public class Diary implements ParamItem { public class Diary implements ParamItem {

View File

@ -2,7 +2,7 @@ package io.github.wulkanowy.api.generic;
public class Lesson { public class Lesson {
private String number = ""; private int number = 0;
private String subject = ""; private String subject = "";
@ -48,12 +48,13 @@ public class Lesson {
private boolean isExemption = false; private boolean isExemption = false;
public String getNumber() { public int getNumber() {
return number; return number;
} }
public void setNumber(String number) { public Lesson setNumber(int number) {
this.number = number; this.number = number;
return this;
} }
public String getSubject() { public String getSubject() {

View File

@ -1,6 +1,6 @@
package io.github.wulkanowy.api; package io.github.wulkanowy.api.generic;
interface ParamItem { public interface ParamItem {
ParamItem setId(String id); ParamItem setId(String id);

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.api; package io.github.wulkanowy.api.generic;
public class Semester implements ParamItem { public class Semester implements ParamItem {

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.api; package io.github.wulkanowy.api.generic;
public class Student implements ParamItem { public class Student implements ParamItem {

View File

@ -5,42 +5,32 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import io.github.wulkanowy.api.SnP; import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException; import io.github.wulkanowy.api.VulcanException;
import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate;
public class GradesList { public class GradesList {
private static final String GRADES_PAGE_URL = "Oceny/Wszystkie?details=2&okres="; private static final String GRADES_PAGE_URL = "Oceny/Wszystkie?details=2&okres=";
private SnP snp; private SnP snp;
private List<Grade> grades = new ArrayList<>();
public GradesList(SnP snp) { public GradesList(SnP snp) {
this.snp = snp; this.snp = snp;
} }
private String getGradesPageUrl() { public List<Grade> getAll(String semester) throws IOException, VulcanException {
return GRADES_PAGE_URL; Document gradesPage = snp.getSnPPageDocument(GRADES_PAGE_URL + semester);
}
public List<Grade> getAll() throws IOException, ParseException, VulcanException {
return getAll("");
}
public List<Grade> getAll(String semester) throws IOException, ParseException, VulcanException {
Document gradesPage = snp.getSnPPageDocument(getGradesPageUrl() + semester);
Elements gradesRows = gradesPage.select(".ocenySzczegoly-table > tbody > tr"); Elements gradesRows = gradesPage.select(".ocenySzczegoly-table > tbody > tr");
List<Grade> grades = new ArrayList<>();
for (Element row : gradesRows) { for (Element row : gradesRows) {
if ("Brak ocen".equals(row.select("td:nth-child(2)").text())) { if ("Brak ocen".equals(row.select("td:nth-child(2)").text())) {
continue; continue;
@ -52,13 +42,13 @@ public class GradesList {
return grades; return grades;
} }
private Grade getGrade(Element row) throws ParseException { private Grade getGrade(Element row) {
String descriptions = row.select("td:nth-child(3)").text(); String descriptions = row.select("td:nth-child(3)").text();
String symbol = descriptions.split(", ")[0]; String symbol = descriptions.split(", ")[0];
String description = descriptions.replaceFirst(Pattern.quote(symbol), "").replaceFirst(", ", ""); String description = descriptions.replaceFirst(Pattern.quote(symbol), "").replaceFirst(", ", "");
String color = getColor(row.select("td:nth-child(2) span.ocenaCzastkowa").attr("style")); 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() return new Grade()
.setSubject(row.select("td:nth-child(1)").text()) .setSubject(row.select("td:nth-child(1)").text())
@ -82,12 +72,4 @@ public class GradesList {
return color; 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);
}
} }

View File

@ -5,10 +5,13 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser; import org.jsoup.parser.Parser;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import io.github.wulkanowy.api.Client; import io.github.wulkanowy.api.Client;
import io.github.wulkanowy.api.NotLoggedInErrorException;
import io.github.wulkanowy.api.VulcanException; import io.github.wulkanowy.api.VulcanException;
public class Login { public class Login {
@ -20,6 +23,8 @@ public class Login {
private Client client; private Client client;
private static final Logger logger = LoggerFactory.getLogger(Login.class);
public Login(Client client) { public Login(Client client) {
this.client = client; this.client = client;
} }
@ -27,6 +32,11 @@ public class Login {
public String login(String email, String password, String symbol) throws VulcanException, IOException { public String login(String email, String password, String symbol) throws VulcanException, IOException {
Document certDoc = sendCredentials(email, password); Document certDoc = sendCredentials(email, password);
if ("Błąd".equals(certDoc.title())) {
client.clearCookies();
throw new NotLoggedInErrorException(certDoc.body().text());
}
return sendCertificate(certDoc, symbol); return sendCertificate(certDoc, symbol);
} }
@ -36,29 +46,29 @@ public class Login {
{"Password", password} {"Password", password}
}; };
String nextUrl = LOGIN_PAGE_URL; Document nextDoc = sendCredentialsData(credentials, LOGIN_PAGE_URL);
Document loginPage = client.getPageByUrl(nextUrl, false);
Element formFirst = loginPage.select("#form1").first(); Element errorMessage = nextDoc.selectFirst(".ErrorMessage, #ErrorTextLabel");
if (null != formFirst) { // on adfs login
Document formSecond = client.postPageByUrl(
formFirst.attr("abs:action"),
getFormStateParams(formFirst, "", "")
);
credentials = getFormStateParams(formSecond, email, password);
nextUrl = formSecond.select("#form1").first().attr("abs:action");
} else if (!"Logowanie".equals(loginPage.select("#h1Default").text())) {
throw new VulcanException("Expected login page, got page with title: " + loginPage.title());
}
Document html = client.postPageByUrl(nextUrl, credentials);
Element errorMessage = html.select(".ErrorMessage, #ErrorTextLabel").first();
if (null != errorMessage) { if (null != errorMessage) {
throw new BadCredentialsException(errorMessage.text()); throw new BadCredentialsException(errorMessage.text());
} }
return html; return nextDoc;
}
private Document sendCredentialsData(String[][] credentials, String nextUrl) throws IOException, VulcanException {
Element formFirst = client.getPageByUrl(nextUrl, false).selectFirst("#form1");
if (null != formFirst) { // only on adfs login
Document formSecond = client.postPageByUrl(
formFirst.attr("abs:action"),
getFormStateParams(formFirst, "", "")
);
credentials = getFormStateParams(formSecond, credentials[0][1], credentials[1][1]);
nextUrl = formSecond.selectFirst("#form1").attr("abs:action");
}
return client.postPageByUrl(nextUrl, credentials);
} }
private String[][] getFormStateParams(Element form, String email, String password) { private String[][] getFormStateParams(Element form, String email, String password) {
@ -77,18 +87,13 @@ public class Login {
} }
String sendCertificate(Document doc, String defaultSymbol) throws IOException, VulcanException { String sendCertificate(Document doc, String defaultSymbol) throws IOException, VulcanException {
String certificate = doc.select("input[name=wresult]").val(); client.setSymbol(findSymbol(defaultSymbol, doc.select("input[name=wresult]").val()));
if ("".equals(certificate)) {
throw new VulcanException("Expected certificate, got empty string. Page title: " + doc.title());
}
client.setSymbol(findSymbol(defaultSymbol, certificate));
Document targetDoc = sendCertData(doc); Document targetDoc = sendCertData(doc);
String title = targetDoc.title(); String title = targetDoc.title();
if ("Working...".equals(title)) { // on adfs login if ("Working...".equals(title)) { // on adfs login
logger.info("ADFS login");
title = sendCertData(targetDoc).title(); title = sendCertData(targetDoc).title();
} }
@ -97,6 +102,7 @@ public class Login {
} }
if (!"Uonet+".equals(title)) { if (!"Uonet+".equals(title)) {
logger.debug("Login failed. Body: {}", targetDoc.body());
throw new LoginErrorException("Expected page title `UONET+`, got " + title); throw new LoginErrorException("Expected page title `UONET+`, got " + title);
} }
@ -106,10 +112,6 @@ public class Login {
private Document sendCertData(Document doc) throws IOException, VulcanException { private Document sendCertData(Document doc) throws IOException, VulcanException {
String url = doc.select("form[name=hiddenform]").attr("action"); String url = doc.select("form[name=hiddenform]").attr("action");
if (!doc.title().equals("Working...")) {
throw new VulcanException("Expected certificate page, got page with title: " + doc.title());
}
return client.postPageByUrl(url.replaceFirst("Default", "{symbol}"), new String[][]{ return client.postPageByUrl(url.replaceFirst("Default", "{symbol}"), new String[][]{
{"wa", "wsignin1.0"}, {"wa", "wsignin1.0"},
{"wresult", doc.select("input[name=wresult]").val()}, {"wresult", doc.select("input[name=wresult]").val()},

View File

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

View File

@ -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<Device> {
val items = snp.getSnPPageDocument(DEVICES_LIST_URL).select("table tbody tr")
val devices: MutableList<Device> = 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
}
}

View File

@ -14,9 +14,7 @@ public class AchievementsList {
private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie"; private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie";
private SnP snp = null; private SnP snp;
private List<String> achievements = new ArrayList<>();
public AchievementsList(SnP snp) { public AchievementsList(SnP snp) {
this.snp = snp; this.snp = snp;
@ -27,6 +25,8 @@ public class AchievementsList {
.select(".mainContainer > div").get(1); .select(".mainContainer > div").get(1);
Elements items = pageFragment.select("article"); Elements items = pageFragment.select("article");
List<String> achievements = new ArrayList<>();
for (Element item : items) { for (Element item : items) {
achievements.add(item.text()); achievements.add(item.text());
} }

View File

@ -10,13 +10,13 @@ import java.util.List;
import io.github.wulkanowy.api.SnP; import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException; import io.github.wulkanowy.api.VulcanException;
import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate;
public class NotesList { public class NotesList {
private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie"; private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie";
private SnP snp = null; private SnP snp;
private List<Note> notes = new ArrayList<>();
public NotesList(SnP snp) { public NotesList(SnP snp) {
this.snp = snp; this.snp = snp;
@ -28,10 +28,12 @@ public class NotesList {
Elements items = pageFragment.select("article"); Elements items = pageFragment.select("article");
Elements dates = pageFragment.select("h2"); Elements dates = pageFragment.select("h2");
List<Note> notes = new ArrayList<>();
int index = 0; int index = 0;
for (Element item : items) { for (Element item : items) {
notes.add(new Note() notes.add(new Note()
.setDate(dates.get(index++).text()) .setDate(getFormattedDate(dates.get(index++).text()))
.setTeacher(snp.getRowDataChildValue(item, 1)) .setTeacher(snp.getRowDataChildValue(item, 1))
.setCategory(snp.getRowDataChildValue(item, 2)) .setCategory(snp.getRowDataChildValue(item, 2))
.setContent(snp.getRowDataChildValue(item, 3)) .setContent(snp.getRowDataChildValue(item, 3))

View File

@ -11,7 +11,7 @@ public class SchoolInfo {
private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele"; private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele";
private SnP snp = null; private SnP snp;
public SchoolInfo(SnP snp) { public SchoolInfo(SnP snp) {
this.snp = snp; this.snp = snp;

View File

@ -15,7 +15,7 @@ public class TeachersInfo {
private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele"; private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele";
private SnP snp = null; private SnP snp;
public TeachersInfo(SnP snp) { public TeachersInfo(SnP snp) {
this.snp = snp; this.snp = snp;

View File

@ -3,36 +3,39 @@ package io.github.wulkanowy.api.timetable;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
import io.github.wulkanowy.api.SnP; import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException; import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.api.generic.Lesson; import io.github.wulkanowy.api.generic.Lesson;
import io.github.wulkanowy.api.generic.Week; 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 { 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 SnP snp;
private static final Logger logger = LoggerFactory.getLogger(Timetable.class);
public Timetable(SnP snp) { public Timetable(SnP snp) {
this.snp = snp; this.snp = snp;
} }
public Week<TimetableDay> getWeekTable() throws IOException, ParseException, VulcanException { public Week<TimetableDay> getWeekTable() throws IOException, VulcanException {
return getWeekTable(""); return getWeekTable("");
} }
public Week<TimetableDay> getWeekTable(final String tick) throws IOException, ParseException, VulcanException { public Week<TimetableDay> getWeekTable(final String date) throws IOException, VulcanException {
Element table = snp.getSnPPageDocument(TIMETABLE_PAGE_URL + tick) Element table = snp.getSnPPageDocument(TIMETABLE_PAGE_URL + getDateAsTick(date))
.select(".mainContainer .presentData").first(); .select(".mainContainer .presentData").first();
List<TimetableDay> days = getDays(table.select("thead th")); List<TimetableDay> days = getDays(table.select("thead th"));
@ -44,19 +47,20 @@ public class Timetable {
.setDays(days); .setDays(days);
} }
private List<TimetableDay> getDays(Elements tableHeaderCells) throws ParseException { private List<TimetableDay> getDays(Elements tableHeaderCells) {
List<TimetableDay> days = new ArrayList<>(); List<TimetableDay> 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("<br>"); String[] dayHeaderCell = tableHeaderCells.get(i).html().split("<br>");
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT);
Date d = sdf.parse(dayHeaderCell[1].trim());
sdf.applyPattern("yyyy-MM-dd");
TimetableDay day = new TimetableDay(); TimetableDay day = new TimetableDay();
day.setDayName(dayHeaderCell[0]); day.setDayName(dayHeaderCell[0]);
day.setDate(sdf.format(d)); day.setDate(getFormattedDate(dayHeaderCell[1].trim()));
if (tableHeaderCells.get(i).hasClass("free-day")) { if (tableHeaderCells.get(i).hasClass("free-day")) {
day.setFreeDay(true); day.setFreeDay(true);
@ -81,7 +85,7 @@ public class Timetable {
lesson.setStartTime(startEndEnd[0]); lesson.setStartTime(startEndEnd[0]);
lesson.setEndTime(startEndEnd[1]); lesson.setEndTime(startEndEnd[1]);
lesson.setDate(days.get(i - 2).getDate()); lesson.setDate(days.get(i - 2).getDate());
lesson.setNumber(hours.get(0).text()); lesson.setNumber(Integer.valueOf(hours.get(0).text()));
addLessonDetails(lesson, hours.get(i).select("div")); addLessonDetails(lesson, hours.get(i).select("div"));
@ -98,8 +102,10 @@ public class Timetable {
addLessonInfoFromElement(lesson, e.first()); addLessonInfoFromElement(lesson, e.first());
break; break;
case 2: case 2:
Element span = e.last().select("span").first(); Element span = e.last().selectFirst("span");
if (span.hasClass(LessonTypes.CLASS_MOVED_OR_CANCELED)) { if (null == span) {
addLessonInfoFromElement(lesson, e.first());
} else if (span.hasClass(LessonTypes.CLASS_MOVED_OR_CANCELED)) {
lesson.setNewMovedInOrChanged(true); lesson.setNewMovedInOrChanged(true);
lesson.setDescription("poprzednio: " + getLessonAndGroupInfoFromSpan(span)[0]); lesson.setDescription("poprzednio: " + getLessonAndGroupInfoFromSpan(span)[0]);
addLessonInfoFromElement(lesson, e.first()); addLessonInfoFromElement(lesson, e.first());
@ -120,7 +126,9 @@ public class Timetable {
Elements warn = e.select(".uwaga-panel"); Elements warn = e.select(".uwaga-panel");
if (!warn.isEmpty()) { 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); e.remove(1);
} }
} }
@ -128,6 +136,11 @@ public class Timetable {
private void addLessonInfoFromElement(Lesson lesson, Element e) { private void addLessonInfoFromElement(Lesson lesson, Element e) {
Elements spans = e.select("span"); Elements spans = e.select("span");
if (spans.isEmpty()) {
logger.warn("Lesson span is empty");
return;
}
addTypeInfo(lesson, spans); addTypeInfo(lesson, spans);
addNormalLessonInfo(lesson, spans); addNormalLessonInfo(lesson, spans);
addChangesInfo(lesson, spans); addChangesInfo(lesson, spans);
@ -214,6 +227,10 @@ public class Timetable {
} }
private String[] getLessonAndGroupInfoFromSpan(Element span) { private String[] getLessonAndGroupInfoFromSpan(Element span) {
if (!span.text().contains("[")) {
return new String[] {span.text(), ""};
}
String[] subjectNameArray = span.text().split(" "); String[] subjectNameArray = span.text().split(" ");
String groupName = subjectNameArray[subjectNameArray.length - 1]; String groupName = subjectNameArray[subjectNameArray.length - 1];

View File

@ -25,7 +25,7 @@ public class ClientTest {
Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-success.html")); 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) @Test(expected = VulcanOfflineException.class)
@ -34,7 +34,7 @@ public class ClientTest {
Document doc = Jsoup.parse(getFixtureAsString("login/PrzerwaTechniczna.html")); Document doc = Jsoup.parse(getFixtureAsString("login/PrzerwaTechniczna.html"));
client.checkForErrors(doc); client.checkForErrors(doc, 200);
} }
@Test(expected = NotLoggedInErrorException.class) @Test(expected = NotLoggedInErrorException.class)
@ -43,7 +43,7 @@ public class ClientTest {
Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-notLoggedIn.html")); Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-notLoggedIn.html"));
client.checkForErrors(doc); client.checkForErrors(doc, 200);
} }
@Test @Test

View File

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

View File

@ -11,6 +11,8 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import io.github.wulkanowy.api.generic.Semester;
public class StudentAndParentTest { public class StudentAndParentTest {
private Client client; private Client client;
@ -53,7 +55,7 @@ public class StudentAndParentTest {
snp.getSnpHomePageUrl()); snp.getSnpHomePageUrl());
} }
@Test(expected = NotLoggedInErrorException.class) @Test(expected = VulcanException.class)
public void getSnpPageUrlWithWrongPage() throws Exception { public void getSnpPageUrlWithWrongPage() throws Exception {
Document wrongPageDocument = Jsoup.parse( Document wrongPageDocument = Jsoup.parse(
FixtureHelper.getAsString(getClass().getResourceAsStream("OcenyWszystkie-semester.html")) FixtureHelper.getAsString(getClass().getResourceAsStream("OcenyWszystkie-semester.html"))
@ -81,7 +83,7 @@ public class StudentAndParentTest {
snp.getExtractedIdFromUrl("https://uonetplus-opiekun.vulcan.net.pl/demoupowiat/demo12345/Start/Index/")); snp.getExtractedIdFromUrl("https://uonetplus-opiekun.vulcan.net.pl/demoupowiat/demo12345/Start/Index/"));
} }
@Test(expected = NotLoggedInErrorException.class) @Test(expected = VulcanException.class)
public void getExtractedIDNotLoggedTest() throws Exception { public void getExtractedIDNotLoggedTest() throws Exception {
Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); Mockito.when(client.getHost()).thenReturn("vulcan.net.pl");
StudentAndParent snp = new StudentAndParent(client, "symbol", null, null); StudentAndParent snp = new StudentAndParent(client, "symbol", null, null);
@ -129,8 +131,8 @@ public class StudentAndParentTest {
@Test @Test
public void getDiariesAndStudentTest() throws IOException, VulcanException { public void getDiariesAndStudentTest() throws IOException, VulcanException {
Document snpHome = Jsoup.parse(FixtureHelper.getAsString( String input = FixtureHelper.getAsString(getClass().getResourceAsStream("WitrynaUczniaIRodzica.html"));
getClass().getResourceAsStream("StudentAndParent.html"))); Document snpHome = Jsoup.parse(input);
client = Mockito.mock(Client.class); client = Mockito.mock(Client.class);
Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(snpHome); Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(snpHome);

View File

@ -5,6 +5,8 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.mockito.Mockito; import org.mockito.Mockito;
import io.github.wulkanowy.api.generic.Semester;
public abstract class StudentAndParentTestCase { public abstract class StudentAndParentTestCase {
protected StudentAndParent getSnp(String fixtureFileName) throws Exception { protected StudentAndParent getSnp(String fixtureFileName) throws Exception {

View File

@ -19,12 +19,12 @@ public class GradesListTest extends StudentAndParentTestCase {
@Test @Test
public void getAllTest() throws Exception { 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 @Test
public void getSubjectTest() throws Exception { public void getSubjectTest() throws Exception {
List<Grade> list = filled.getAll(); List<Grade> list = filled.getAll("");
Assert.assertEquals("Zajęcia z wychowawcą", list.get(0).getSubject()); Assert.assertEquals("Zajęcia z wychowawcą", list.get(0).getSubject());
Assert.assertEquals("Język angielski", list.get(3).getSubject()); Assert.assertEquals("Język angielski", list.get(3).getSubject());
@ -34,7 +34,7 @@ public class GradesListTest extends StudentAndParentTestCase {
@Test @Test
public void getValueTest() throws Exception { public void getValueTest() throws Exception {
List<Grade> list = filled.getAll(); List<Grade> list = filled.getAll("");
Assert.assertEquals("5", list.get(0).getValue()); Assert.assertEquals("5", list.get(0).getValue());
Assert.assertEquals("5", list.get(3).getValue()); Assert.assertEquals("5", list.get(3).getValue());
@ -44,7 +44,7 @@ public class GradesListTest extends StudentAndParentTestCase {
@Test @Test
public void getColorTest() throws Exception { public void getColorTest() throws Exception {
List<Grade> list = filled.getAll(); List<Grade> list = filled.getAll("");
Assert.assertEquals("000000", list.get(0).getColor()); Assert.assertEquals("000000", list.get(0).getColor());
Assert.assertEquals("1289F7", list.get(3).getColor()); Assert.assertEquals("1289F7", list.get(3).getColor());
@ -54,7 +54,7 @@ public class GradesListTest extends StudentAndParentTestCase {
@Test @Test
public void getSymbolTest() throws Exception { public void getSymbolTest() throws Exception {
List<Grade> list = filled.getAll(); List<Grade> list = filled.getAll("");
Assert.assertEquals("A1", list.get(0).getSymbol()); Assert.assertEquals("A1", list.get(0).getSymbol());
Assert.assertEquals("BW3", list.get(3).getSymbol()); Assert.assertEquals("BW3", list.get(3).getSymbol());
@ -65,7 +65,7 @@ public class GradesListTest extends StudentAndParentTestCase {
@Test @Test
public void getDescriptionTest() throws Exception { public void getDescriptionTest() throws Exception {
List<Grade> list = filled.getAll(); List<Grade> list = filled.getAll("");
Assert.assertEquals("Dzień Kobiet w naszej klasie", list.get(0).getDescription()); Assert.assertEquals("Dzień Kobiet w naszej klasie", list.get(0).getDescription());
Assert.assertEquals("Writing", list.get(3).getDescription()); Assert.assertEquals("Writing", list.get(3).getDescription());
@ -76,7 +76,7 @@ public class GradesListTest extends StudentAndParentTestCase {
@Test @Test
public void getWeightTest() throws Exception { public void getWeightTest() throws Exception {
List<Grade> list = filled.getAll(); List<Grade> list = filled.getAll("");
Assert.assertEquals("1,00", list.get(0).getWeight()); Assert.assertEquals("1,00", list.get(0).getWeight());
Assert.assertEquals("3,00", list.get(3).getWeight()); Assert.assertEquals("3,00", list.get(3).getWeight());
@ -86,7 +86,7 @@ public class GradesListTest extends StudentAndParentTestCase {
@Test @Test
public void getDateTest() throws Exception { public void getDateTest() throws Exception {
List<Grade> list = filled.getAll(); List<Grade> list = filled.getAll("");
Assert.assertEquals("2017-03-21", list.get(0).getDate()); Assert.assertEquals("2017-03-21", list.get(0).getDate());
Assert.assertEquals("2017-06-02", list.get(3).getDate()); Assert.assertEquals("2017-06-02", list.get(3).getDate());
@ -96,7 +96,7 @@ public class GradesListTest extends StudentAndParentTestCase {
@Test @Test
public void getTeacherTest() throws Exception { public void getTeacherTest() throws Exception {
List<Grade> list = filled.getAll(); List<Grade> list = filled.getAll("");
Assert.assertEquals("Patryk Maciejewski", list.get(0).getTeacher()); Assert.assertEquals("Patryk Maciejewski", list.get(0).getTeacher());
Assert.assertEquals("Oliwia Woźniak", list.get(3).getTeacher()); Assert.assertEquals("Oliwia Woźniak", list.get(3).getTeacher());

View File

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

View File

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

View File

@ -30,8 +30,8 @@ public class NotesListTest extends StudentAndParentTestCase {
public void getDateTest() throws Exception { public void getDateTest() throws Exception {
List<Note> filledList = filled.getAllNotes(); List<Note> filledList = filled.getAllNotes();
Assert.assertEquals("06.06.2017", filledList.get(0).getDate()); Assert.assertEquals("2017-06-06", filledList.get(0).getDate());
Assert.assertEquals("01.10.2016", filledList.get(2).getDate()); Assert.assertEquals("2016-10-01", filledList.get(2).getDate());
} }
@Test @Test

View File

@ -83,11 +83,11 @@ public class TimetableTest extends StudentAndParentTestCase {
@Test @Test
public void getLessonNumberTest() throws Exception { public void getLessonNumberTest() throws Exception {
Assert.assertEquals("2", std.getWeekTable().getDay(0).getLesson(1).getNumber()); Assert.assertEquals(2, std.getWeekTable().getDay(0).getLesson(1).getNumber());
Assert.assertEquals("5", std.getWeekTable().getDay(2).getLesson(4).getNumber()); Assert.assertEquals(5, std.getWeekTable().getDay(2).getLesson(4).getNumber());
Assert.assertEquals("0", full.getWeekTable().getDay(0).getLesson(0).getNumber()); Assert.assertEquals(0, full.getWeekTable().getDay(0).getLesson(0).getNumber());
Assert.assertEquals("13", full.getWeekTable().getDay(4).getLesson(13).getNumber()); Assert.assertEquals(13, full.getWeekTable().getDay(4).getLesson(13).getNumber());
Assert.assertEquals("3", holidays.getWeekTable().getDay(3).getLesson(3).getNumber()); Assert.assertEquals(3, holidays.getWeekTable().getDay(3).getLesson(3).getNumber());
} }
@Test @Test
@ -105,6 +105,7 @@ public class TimetableTest extends StudentAndParentTestCase {
Assert.assertEquals("Zajęcia techniczne", std.getWeekTable().getDay(2).getLesson(4).getSubject()); 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("Wychowanie fizyczne", std.getWeekTable().getDay(1).getLesson(1).getSubject());
Assert.assertEquals("Język angielski", full.getWeekTable().getDay(0).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 do życia w rodzinie", full.getWeekTable().getDay(2).getLesson(0).getSubject());
Assert.assertEquals("Wychowanie fizyczne", full.getWeekTable().getDay(3).getLesson(1).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()); 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("poprzednio: Wychowanie fizyczne", full.getWeekTable().getDay(4).getLesson(2).getDescription());
Assert.assertEquals("egzamin", full.getWeekTable().getDay(3).getLesson(0).getDescription()); Assert.assertEquals("egzamin", full.getWeekTable().getDay(3).getLesson(0).getDescription());
Assert.assertEquals("", full.getWeekTable().getDay(4).getLesson(1).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()); Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getDescription());
} }

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>Witryna ucznia i rodzica dostęp mobilny</title>
</head>
<body>
<main class="mainContainer">
<h1>Dostęp mobilny</h1>
<article>
<h2>Zarejestrowane urządzenia</h2>
</article>
<table>
<thead>
<tr>
<th>Urządzenie</th>
<th>Data rejestracji</th>
<th></th>
</tr>
</thead>
<tbody>
<tr class="text-center">
<td>google Android SDK built for x86 (Android 8.1.0)</td>
<td>20.01.2018 godz: 22:35:30</td>
<td class="cellWithButton">
<a href="/Default/123456/DostepMobilny.mvc/Wyrejestruj/321" class="button">Wyrejestruj</a>
</td>
</tr>
<tr class="text-center">
<td>google (Android SDK) built for x86 (Android 8.1.0)</td>
<td>20.01.2018 godz: 22:35:30</td>
<td class="cellWithButton">
<a href="/Default/123456/DostepMobilny.mvc/Wyrejestruj/213" class="button">Wyrejestruj</a>
</td>
</tr>
</tbody>
</table>
</main>
<footer>wersja: 18.01.0001.27311</footer>
</body>
</html>

View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>Witryna ucznia i rodzica Rejestracja urządzenia mobilnego</title>
<style>
.blockElement {
display: block;
}
</style>
</head>
<body>
<main class="mainContainer">
<h1>Rejestracja urządzenia mobilnego</h1>
<article class="text-center" id="rejestracja-formularz">
<span class="blockElement">Za pomocą aplikacji &quot;Dzienniczek+&quot; zeskanuj kod QR.</span>
<img src=" data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAYAAACAvzbMAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gUdCTohm9rlswAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAFKklEQVR42u3dYYqDQAyA0abk/lfO/l+6LLZYksx7J3C09SPgaFRVPQDgoqdTAICAACAgAAgIAAICAAICgIAAICAACAgAAgIAAgKAgAAgIAAICAACAgACAoCAACAgAAgIAAICAAICgIAAICAACAgAAgIAAgKAgAAgIAAICAACAgACAoCAACAgAAgIAAICAFdltwOKiNUnvKqOXj/4/9y3fhMIACMICAACAoCAACAgAAgIAAgIAJ/IaQfc7Tno3+5+Dr37+sH/p+/6TSAACAgAAgKAgACAgAAgIAAICADj5bYF2YcBuH+YQAAQEAAEBAAEBAABAUBAABAQAAQEAAQEAAEBQEAAEBAABAQABAQAAQFAQAAQEAAEBAAEBAABAUBAABAQAAQEAP6R2xZUVa4q4P5hAgFAQAAQEAAQEAAEBAABAUBAANho3D6QiHDVAPcPEwgAAgKAgACAgAAgIAAICAACAsCJ2u0D8T5+wP3DBAKAgACAgAAgIAAICAACAoCAAMBL7faBeF8/4P7wWrd9LiYQAAQEAAEBQEAAEBAAEBAABASAr8tpB+x9/7DX6f/vaftcTCAACAgAAgKAgAAgIAAgIAAICAACAoCAACAgACAgAAgIAAICgIAAICAAICAACAgAAgKAgAAgIAAgIAAICAACAoCAACAgACAgAAgIAAICgIAAICAAICAACAgAAgKAgAAgIAAgIAAICAACAoCAAICAACAgAAgIAAICgIAAgIAAICAACAgAAgKAgACAgAAgIAAICAACAoCAAICAACAgAAgIAAICgIAAgIAAICAACAgAAgKAgACAgAAgIAAICAACAoCAAICAACAgAAgIAAICgIA4BQAICAACAoCAACAgACAgAAgIAAICgIAAICAAICAACAgAAgKAgAAgIAAgIAAICAACAoCAACAgACAgAAgIAAICgIAAICAAICAACAgAAgKAgAAgIAAgIAAICAACAoCAACAgACAgAAgIAAICgIAAgIAAICAACAgAAgKAgACAgAAgIAAICAACAoCAAICAACAgAAgIAAICgIAAgIAAICAACAgAAgKAgACAgAAgIAAICAACAoCAAICAACAgAAgIAAICgIAAgIAAICAACAgAAgKAgACAgAAgIAAICAACAgACAoCAACAgAAgIAAICAAICgIAAICAACAgAAgIAAgKAgAAgIAAICAACAgACAoCAACAgAAgIAAICAAICgIAAICAACAgAAgIAAgKAgAAgIAAICAACAgACAoCAANBWTjvgiDj6glWVXy1gAgFAQAAQEAAQEAAEBAABAUBAuOb0x5CBPdrtAzl9n4PAACYQAAQEAAQEAAEBQEAAEBAABAQA/tBuH8j2fRC+5wGYQAAQEAAQEAAEBAABAUBAAEBAAHhTTjvg7vsofM8DMIEAgIAAICAACAgAAgKAgACAgABwk9y2oLv3YfieB4AJBAABAUBAABAQAAQEAAQEAAEBQEAAEBAABAQABAQAAQFAQAAQEAAEBAAEBAABAUBAABAQAAQEAAQEAAEBQEAAGC+3LaiqXFUAEwgAAgKAgACAgAAgIAAICAACAsBG4/aBRMTRF+z09QMmEAAEBAABAQABAUBAABAQAAQEgINF+YAGACYQAAQEAAEBQEAAQEAAEBAABAQAAQFAQABAQAAQEAAEBAABAUBAAEBAABAQAAQEAAEBQEAAQEAAEBAABAQAAQFAQABAQAAQEAAEBAABAUBAAEBAABAQAAQEAAEBQEAAQEAA+IYf8fZKNX0RrMQAAAAASUVORK5CYII="
alt="Kod QR" title="Kod QR" height="400" width="400"/>
<span class="blockElement">Token: 3S1A1B2C</span>
<span class="blockElement">Symbol: Default</span>
<span class="blockElement">PIN: 1234567</span>
</article>
</main>
<footer>wersja: 18.01.0001.27311</footer>
</body>
</html>

View File

@ -319,7 +319,16 @@
<span></span> <span></span>
</div> </div>
</td> </td>
<td></td> <td>
<div>
<span class="x-treelabel-ppl x-treelabel-inv">Język polski</span>
<span class="x-treelabel-ppl x-treelabel-inv"> </span>
<span class="x-treelabel-ppl x-treelabel-inv">16</span>
<span class="random-class">(oddział nieobecny)</span>
</div>
<button type="button" class="uwaga-btn">Uwaga</button>
<div class="uwaga-panel">opis w uwadze bez klasy w spanie</div>
</td>
</tr> </tr>
<tr> <tr>
<td>5</td> <td>5</td>
@ -355,7 +364,20 @@
<span></span> <span></span>
</div> </div>
</td> </td>
<td></td> <td>
<div>
<span class="">Tworzenie i administrowanie bazami danych [zaw2]</span>
<span class=""></span>
<span class=""></span>
<span class=""></span>
</div>
<div>
<span class="x-treelabel-ppl x-treelabel-inv">Zajęcia z wychowawcą</span>
<span class="x-treelabel-ppl x-treelabel-inv">Małgorzata Kowal</span>
<span class="x-treelabel-ppl x-treelabel-inv">43</span>
<span class="x-treelabel-rlz">(zmiana organizacji zajęć)</span>
</div>
</td>
</tr> </tr>
<tr> <tr>
<td>6</td> <td>6</td>
@ -461,7 +483,18 @@
<tr> <tr>
<td>9</td> <td>9</td>
<td>14:50 15:35</td> <td>14:50 15:35</td>
<td></td> <td>
<div>
<span class="x-treelabel-ppl x-treelabel-zas">Wychowanie fizyczne [zaw2]</span>
<span class="x-treelabel-ppl x-treelabel-zas"></span>
<span class="x-treelabel-ppl x-treelabel-zas"></span>
<span class="x-treelabel-ppl x-treelabel-zas">G3</span>
<span class="x-treelabel-rlz">(przeniesiona z lekcji 7, 01.12.2017)</span>
</div>
<div>
<!--<span>WTF</span>-->
</div>
</td>
<td> <td>
<div> <div>
<span class="x-treelabel-ppl x-treelabel-inv">Język niemiecki [J1]</span> <span class="x-treelabel-ppl x-treelabel-inv">Język niemiecki [J1]</span>

View File

@ -9,7 +9,7 @@ buildscript {
classpath "org.greenrobot:greendao-gradle-plugin:$greenDaoGradle" classpath "org.greenrobot:greendao-gradle-plugin:$greenDaoGradle"
classpath "io.fabric.tools:gradle:$fabricGradle" classpath "io.fabric.tools:gradle:$fabricGradle"
classpath "com.google.gms:oss-licenses:0.9.2" 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"
} }
} }
@ -41,8 +41,8 @@ android {
testApplicationId "io.github.tests.wulkanowy" testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 26 targetSdkVersion 26
versionCode 10 versionCode 14
versionName "0.4.2" versionName "0.5.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
playAccountConfig = playAccountConfigs.defaultAccountConfig playAccountConfig = playAccountConfigs.defaultAccountConfig
@ -91,7 +91,7 @@ play {
} }
greendao { greendao {
schemaVersion 27 schemaVersion 28
generateTests = true generateTests = true
} }
@ -118,6 +118,9 @@ dependencies {
implementation "com.jakewharton.threetenabp:threetenabp:$threeTenABP" implementation "com.jakewharton.threetenabp:threetenabp:$threeTenABP"
implementation "com.google.android.gms:play-services-oss-licenses:$ossLicenses" 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") { implementation("com.crashlytics.sdk.android:crashlytics:$crashlyticsSdk@aar") {
transitive = true transitive = true
} }

View File

@ -14,7 +14,6 @@
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/WulkanowyTheme"> android:theme="@style/WulkanowyTheme">
<activity <activity
android:name=".ui.splash.SplashActivity" android:name=".ui.splash.SplashActivity"
@ -31,20 +30,20 @@
android:name=".ui.login.LoginActivity" android:name=".ui.login.LoginActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/title_activity_login" android:label="@string/title_activity_login"
android:theme="@style/WulkanowyTheme.LoginTheme" android:theme="@style/WulkanowyTheme.DarkActionBar"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity <activity
android:name=".ui.main.MainActivity" android:name=".ui.main.MainActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/activity_dashboard_text" android:label="@string/activity_dashboard_text"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/WulkanowyTheme" /> />
<activity <activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity" android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
android:theme="@style/WulkanowyTheme.LoginTheme" /> android:theme="@style/WulkanowyTheme.DarkActionBar" />
<activity <activity
android:name="com.google.android.gms.oss.licenses.OssLicensesActivity" android:name="com.google.android.gms.oss.licenses.OssLicensesActivity"
android:theme="@style/WulkanowyTheme.LoginTheme" /> android:theme="@style/WulkanowyTheme.DarkActionBar" />
<service <service
android:name=".services.jobs.SyncJob" android:name=".services.jobs.SyncJob"

View File

@ -1,8 +1,7 @@
package io.github.wulkanowy; package io.github.wulkanowy;
import android.app.Application;
import com.crashlytics.android.Crashlytics; import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.answers.Answers;
import com.crashlytics.android.core.CrashlyticsCore; import com.crashlytics.android.core.CrashlyticsCore;
import com.jakewharton.threetenabp.AndroidThreeTen; import com.jakewharton.threetenabp.AndroidThreeTen;
@ -10,18 +9,17 @@ import org.greenrobot.greendao.query.QueryBuilder;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.android.AndroidInjector;
import dagger.android.support.DaggerApplication;
import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.utils.Log;
import io.fabric.sdk.android.Fabric; import io.fabric.sdk.android.Fabric;
import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.di.component.ApplicationComponent; import io.github.wulkanowy.di.DaggerAppComponent;
import io.github.wulkanowy.di.component.DaggerApplicationComponent; import io.github.wulkanowy.utils.FabricUtils;
import io.github.wulkanowy.di.modules.ApplicationModule; import io.github.wulkanowy.utils.LoggerUtils;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
public class WulkanowyApp extends Application { public class WulkanowyApp extends DaggerApplication {
protected ApplicationComponent applicationComponent;
@Inject @Inject
RepositoryContract repository; RepositoryContract repository;
@ -31,12 +29,6 @@ public class WulkanowyApp extends Application {
super.onCreate(); super.onCreate();
AndroidThreeTen.init(this); AndroidThreeTen.init(this);
applicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(this))
.build();
applicationComponent.inject(this);
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
enableDebugLog(); enableDebugLog();
} }
@ -48,27 +40,35 @@ public class WulkanowyApp extends Application {
if (repository.getSharedRepo().isUserLoggedIn()) { if (repository.getSharedRepo().isUserLoggedIn()) {
try { try {
repository.getSyncRepo().initLastUser(); repository.getSyncRepo().initLastUser();
FabricUtils.logLogin("Open app", true);
} catch (Exception e) { } catch (Exception e) {
LogUtils.error("An error occurred when the application was started", e); FabricUtils.logLogin("Open app", false);
Timber.e(e, "An error occurred when the application was started");
} }
} }
} }
private void enableDebugLog() { private void enableDebugLog() {
QueryBuilder.LOG_VALUES = true; QueryBuilder.LOG_VALUES = true;
FlexibleAdapter.enableLogs(Log.Level.DEBUG); FlexibleAdapter.enableLogs(eu.davidea.flexibleadapter.utils.Log.Level.DEBUG);
Timber.plant(new LoggerUtils.DebugLogTree());
} }
private void initializeFabric() { private void initializeFabric() {
Fabric.with(new Fabric.Builder(this) Fabric.with(new Fabric.Builder(this)
.kits(new Crashlytics.Builder() .kits(
.core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()) new Crashlytics.Builder()
.build()) .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
.build(),
new Answers()
)
.debuggable(BuildConfig.DEBUG) .debuggable(BuildConfig.DEBUG)
.build()); .build());
Timber.plant(new LoggerUtils.CrashlyticsTree());
} }
public ApplicationComponent getApplicationComponent() { @Override
return applicationComponent; protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder().create(this);
} }
} }

View File

@ -47,4 +47,10 @@ public class Repository implements RepositoryContract {
public SyncContract getSyncRepo() { public SyncContract getSyncRepo() {
return synchronization; return synchronization;
} }
@Override
public void cleanAllData() {
sharedPref.cleanSharedPref();
database.recreateDatabase();
}
} }

View File

@ -17,4 +17,6 @@ public interface RepositoryContract {
DbContract getDbRepo(); DbContract getDbRepo();
SyncContract getSyncRepo(); SyncContract getSyncRepo();
void cleanAllData();
} }

View File

@ -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.Grade;
import io.github.wulkanowy.data.db.dao.entities.Subject; import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.data.db.dao.entities.Symbol;
import io.github.wulkanowy.data.db.dao.entities.Week; import io.github.wulkanowy.data.db.dao.entities.Week;
public interface DbContract { public interface DbContract {
@ -20,6 +21,8 @@ public interface DbContract {
long getCurrentSymbolId(); long getCurrentSymbolId();
Symbol getCurrentSymbol();
long getCurrentDiaryId(); long getCurrentDiaryId();
long getSemesterId(int name); long getSemesterId(int name);
@ -27,4 +30,6 @@ public interface DbContract {
long getCurrentSemesterId(); long getCurrentSemesterId();
int getCurrentSemesterName(); int getCurrentSemesterName();
void recreateDatabase();
} }

View File

@ -12,6 +12,7 @@ import java.util.Comparator;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan; import io.github.wulkanowy.api.Vulcan;
@ -19,10 +20,9 @@ import io.github.wulkanowy.data.db.dao.entities.DaoMaster;
import io.github.wulkanowy.data.db.dao.migrations.Migration23; 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.Migration26;
import io.github.wulkanowy.data.db.dao.migrations.Migration27; import io.github.wulkanowy.data.db.dao.migrations.Migration27;
import io.github.wulkanowy.data.db.dao.migrations.Migration28;
import io.github.wulkanowy.data.db.shared.SharedPrefContract; import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.di.annotations.ApplicationContext; import timber.log.Timber;
import io.github.wulkanowy.di.annotations.DatabaseInfo;
import io.github.wulkanowy.utils.LogUtils;
@Singleton @Singleton
public class DbHelper extends DaoMaster.OpenHelper { public class DbHelper extends DaoMaster.OpenHelper {
@ -32,7 +32,7 @@ public class DbHelper extends DaoMaster.OpenHelper {
private final Vulcan vulcan; private final Vulcan vulcan;
@Inject @Inject
DbHelper(@ApplicationContext Context context, @DatabaseInfo String dbName, DbHelper(Context context, @Named("dbName") String dbName,
SharedPrefContract sharedPref, Vulcan vulcan) { SharedPrefContract sharedPref, Vulcan vulcan) {
super(context, dbName); super(context, dbName);
this.sharedPref = sharedPref; this.sharedPref = sharedPref;
@ -41,7 +41,7 @@ public class DbHelper extends DaoMaster.OpenHelper {
@Override @Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 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); Database database = new StandardDatabase(db);
recreateDatabase(database); recreateDatabase(database);
} }
@ -54,11 +54,11 @@ public class DbHelper extends DaoMaster.OpenHelper {
for (Migration migration : migrations) { for (Migration migration : migrations) {
if (oldVersion < migration.getVersion()) { if (oldVersion < migration.getVersion()) {
try { 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); migration.runMigration(db, sharedPref, vulcan);
LogUtils.info("Migration " + migration.getVersion() + " complete"); Timber.i("Migration %s complete", migration.getVersion());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); Timber.e(e, "Failed to apply migration");
recreateDatabase(db); recreateDatabase(db);
break; break;
} }
@ -67,7 +67,7 @@ public class DbHelper extends DaoMaster.OpenHelper {
} }
private void recreateDatabase(Database db) { private void recreateDatabase(Database db) {
LogUtils.info("Database is recreating..."); Timber.i("Database is recreating...");
sharedPref.setCurrentUserId(0); sharedPref.setCurrentUserId(0);
DaoMaster.dropAllTables(db, true); DaoMaster.dropAllTables(db, true);
onCreate(db); onCreate(db);
@ -78,6 +78,7 @@ public class DbHelper extends DaoMaster.OpenHelper {
migrations.add(new Migration23()); migrations.add(new Migration23());
migrations.add(new Migration26()); migrations.add(new Migration26());
migrations.add(new Migration27()); migrations.add(new Migration27());
migrations.add(new Migration28());
// Sorting just to be safe, in case other people add migrations in the wrong order. // Sorting just to be safe, in case other people add migrations in the wrong order.
Comparator<Migration> migrationComparator = new Comparator<Migration>() { Comparator<Migration> migrationComparator = new Comparator<Migration>() {

View File

@ -1,9 +1,12 @@
package io.github.wulkanowy.data.db.dao; package io.github.wulkanowy.data.db.dao;
import org.greenrobot.greendao.database.Database;
import java.util.List; import java.util.List;
import javax.inject.Inject; 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.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.DiaryDao; 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.Grade;
@ -12,6 +15,7 @@ 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.SemesterDao;
import io.github.wulkanowy.data.db.dao.entities.StudentDao; 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.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.SymbolDao;
import io.github.wulkanowy.data.db.dao.entities.Week; import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao; import io.github.wulkanowy.data.db.dao.entities.WeekDao;
@ -57,10 +61,15 @@ public class DbRepository implements DbContract {
} }
@Override @Override
public long getCurrentSymbolId() { public Symbol getCurrentSymbol() {
return daoSession.getSymbolDao().queryBuilder().where( return daoSession.getSymbolDao().queryBuilder().where(
SymbolDao.Properties.UserId.eq(sharedPref.getCurrentUserId()) SymbolDao.Properties.UserId.eq(sharedPref.getCurrentUserId())
).unique().getId(); ).unique();
}
@Override
public long getCurrentSymbolId() {
return getCurrentSymbol().getId();
} }
@Override @Override
@ -103,4 +112,12 @@ public class DbRepository implements DbContract {
SemesterDao.Properties.Current.eq(true) SemesterDao.Properties.Current.eq(true)
).unique(); ).unique();
} }
@Override
public void recreateDatabase() {
Database database = daoSession.getDatabase();
DaoMaster.dropAllTables(database, true);
DaoMaster.createAllTables(database, true);
}
} }

View File

@ -5,6 +5,7 @@ import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated; import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id; import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Index; import org.greenrobot.greendao.annotation.Index;
import org.greenrobot.greendao.annotation.OrderBy;
import org.greenrobot.greendao.annotation.Property; import org.greenrobot.greendao.annotation.Property;
import org.greenrobot.greendao.annotation.ToMany; import org.greenrobot.greendao.annotation.ToMany;
@ -35,9 +36,11 @@ public class Day {
@Property(nameInDb = "free_day_name") @Property(nameInDb = "free_day_name")
private String freeDayName = ""; private String freeDayName = "";
@OrderBy("number ASC")
@ToMany(referencedJoinProperty = "dayId") @ToMany(referencedJoinProperty = "dayId")
private List<TimetableLesson> timetableLessons; private List<TimetableLesson> timetableLessons;
@OrderBy("number ASC")
@ToMany(referencedJoinProperty = "dayId") @ToMany(referencedJoinProperty = "dayId")
private List<AttendanceLesson> attendanceLessons; private List<AttendanceLesson> attendanceLessons;
@ -50,9 +53,7 @@ public class Day {
@Generated(hash = 2040040024) @Generated(hash = 2040040024)
private transient DaoSession daoSession; private transient DaoSession daoSession;
/** /** Used for active entity operations. */
* Used for active entity operations.
*/
@Generated(hash = 312167767) @Generated(hash = 312167767)
private transient DayDao myDao; private transient DayDao myDao;
@ -185,6 +186,36 @@ public class Day {
attendanceLessons = null; attendanceLessons = null;
} }
/**
* 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 = 1231531946)
public List<Exam> getExams() {
if (exams == null) {
final DaoSession daoSession = this.daoSession;
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
ExamDao targetDao = daoSession.getExamDao();
List<Exam> examsNew = targetDao._queryDay_Exams(id);
synchronized (this) {
if (exams == null) {
exams = examsNew;
}
}
}
return exams;
}
/**
* Resets a to-many relationship, making the next get call to query for a fresh result.
*/
@Generated(hash = 841969952)
public synchronized void resetExams() {
exams = null;
}
/** /**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
* Entity must attached to an entity context. * Entity must attached to an entity context.
@ -221,40 +252,12 @@ public class Day {
myDao.update(this); myDao.update(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 = 1231531946)
public List<Exam> getExams() {
if (exams == null) {
final DaoSession daoSession = this.daoSession;
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
ExamDao targetDao = daoSession.getExamDao();
List<Exam> examsNew = targetDao._queryDay_Exams(id);
synchronized (this) {
if (exams == null) {
exams = examsNew;
}
}
}
return exams;
}
/**
* Resets a to-many relationship, making the next get call to query for a fresh result.
*/
@Generated(hash = 841969952)
public synchronized void resetExams() {
exams = null;
}
/** called by internal mechanisms, do not call yourself. */ /** called by internal mechanisms, do not call yourself. */
@Generated(hash = 1409317752) @Generated(hash = 1409317752)
public void __setDaoSession(DaoSession daoSession) { public void __setDaoSession(DaoSession daoSession) {
this.daoSession = daoSession; this.daoSession = daoSession;
myDao = daoSession != null ? daoSession.getDayDao() : null; myDao = daoSession != null ? daoSession.getDayDao() : null;
} }
} }

View File

@ -1,5 +1,7 @@
package io.github.wulkanowy.data.db.dao.entities; package io.github.wulkanowy.data.db.dao.entities;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.DaoException;
import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated; import org.greenrobot.greendao.annotation.Generated;
@ -12,7 +14,7 @@ import java.io.Serializable;
@Entity( @Entity(
nameInDb = "TimetableLessons", nameInDb = "TimetableLessons",
active = true, active = true,
indexes = {@Index(value = "dayId,date,startTime,endTime", unique = true)} indexes = {@Index(value = "dayId,date,number,startTime,endTime", unique = true)}
) )
public class TimetableLesson implements Serializable { public class TimetableLesson implements Serializable {
@ -25,7 +27,7 @@ public class TimetableLesson implements Serializable {
private Long dayId; private Long dayId;
@Property(nameInDb = "number") @Property(nameInDb = "number")
private String number; private int number = 0;
@Property(nameInDb = "subject") @Property(nameInDb = "subject")
private String subject = ""; private String subject = "";
@ -75,18 +77,15 @@ public class TimetableLesson implements Serializable {
@Generated(hash = 2040040024) @Generated(hash = 2040040024)
private transient DaoSession daoSession; private transient DaoSession daoSession;
/** /** Used for active entity operations. */
* Used for active entity operations.
*/
@Generated(hash = 1119360138) @Generated(hash = 1119360138)
private transient TimetableLessonDao myDao; private transient TimetableLessonDao myDao;
@Generated(hash = 1955911128) @Generated(hash = 1665905034)
public TimetableLesson(Long id, Long dayId, String number, String subject, public TimetableLesson(Long id, Long dayId, int number, String subject, String teacher,
String teacher, String room, String description, String group, String room, String description, String group, String startTime, String endTime,
String startTime, String endTime, String date, boolean empty, String date, boolean empty, boolean divisionIntoGroups, boolean planning,
boolean divisionIntoGroups, boolean planning, boolean realized, boolean realized, boolean movedOrCanceled, boolean newMovedInOrChanged) {
boolean movedOrCanceled, boolean newMovedInOrChanged) {
this.id = id; this.id = id;
this.dayId = dayId; this.dayId = dayId;
this.number = number; this.number = number;
@ -109,7 +108,7 @@ public class TimetableLesson implements Serializable {
@Generated(hash = 1878030142) @Generated(hash = 1878030142)
public TimetableLesson() { public TimetableLesson() {
} }
public Long getId() { public Long getId() {
return this.id; return this.id;
} }
@ -127,11 +126,11 @@ public class TimetableLesson implements Serializable {
return this; return this;
} }
public String getNumber() { public int getNumber() {
return this.number; return this.number;
} }
public TimetableLesson setNumber(String number) { public TimetableLesson setNumber(int number) {
this.number = number; this.number = number;
return this; return this;
} }
@ -262,6 +261,32 @@ public class TimetableLesson implements Serializable {
return this; return this;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TimetableLesson lesson = (TimetableLesson) o;
return new EqualsBuilder()
.append(number, lesson.number)
.append(startTime, lesson.startTime)
.append(endTime, lesson.endTime)
.append(date, lesson.date)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(number)
.append(startTime)
.append(endTime)
.append(date)
.toHashCode();
}
/** /**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
* Entity must attached to an entity context. * Entity must attached to an entity context.

View File

@ -9,8 +9,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import io.github.wulkanowy.api.Diary;
import io.github.wulkanowy.api.Vulcan; 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.dao.DbHelper;
import io.github.wulkanowy.data.db.shared.SharedPrefContract; import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.security.Scrambler; import io.github.wulkanowy.utils.security.Scrambler;

View File

@ -0,0 +1,20 @@
package io.github.wulkanowy.data.db.dao.migrations;
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;
public class Migration28 implements DbHelper.Migration {
@Override
public Integer getVersion() {
return 28;
}
@Override
public void runMigration(final Database db, final SharedPrefContract sharedPref, final Vulcan vulcan) throws Exception {
throw new Exception("No migrations");
}
}

View File

@ -3,8 +3,6 @@ package io.github.wulkanowy.data.db.resources;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import com.crashlytics.android.Crashlytics;
import java.io.IOException; import java.io.IOException;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
@ -15,10 +13,9 @@ import javax.inject.Singleton;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.api.NotLoggedInErrorException; import io.github.wulkanowy.api.NotLoggedInErrorException;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; 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.AppConstant;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.security.CryptoException; import io.github.wulkanowy.utils.security.CryptoException;
import timber.log.Timber;
@Singleton @Singleton
public class ResourcesRepository implements ResourcesContract { public class ResourcesRepository implements ResourcesContract {
@ -26,7 +23,7 @@ public class ResourcesRepository implements ResourcesContract {
private Resources resources; private Resources resources;
@Inject @Inject
ResourcesRepository(@ApplicationContext Context context) { ResourcesRepository(Context context) {
resources = context.getResources(); resources = context.getResources();
} }
@ -42,8 +39,7 @@ public class ResourcesRepository implements ResourcesContract {
@Override @Override
public String getErrorLoginMessage(Exception exception) { public String getErrorLoginMessage(Exception exception) {
LogUtils.error(AppConstant.APP_NAME + " encountered a error", exception); Timber.e(exception,"%s encountered a error", AppConstant.APP_NAME);
Crashlytics.logException(exception);
if (exception instanceof CryptoException) { if (exception instanceof CryptoException) {
return resources.getString(R.string.encrypt_failed_text); return resources.getString(R.string.encrypt_failed_text);

View File

@ -21,6 +21,8 @@ public interface SharedPrefContract {
boolean isShowAttendancePresent(); boolean isShowAttendancePresent();
int getCurrentTheme();
int getServicesInterval(); int getServicesInterval();
boolean isMobileDisable(); boolean isMobileDisable();
@ -28,4 +30,6 @@ public interface SharedPrefContract {
boolean isServicesEnable(); boolean isServicesEnable();
boolean isNotifyEnable(); boolean isNotifyEnable();
void cleanSharedPref();
} }

View File

@ -6,10 +6,9 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton; 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; import io.github.wulkanowy.ui.main.settings.SettingsFragment;
@Singleton @Singleton
@ -24,7 +23,7 @@ public class SharedPrefRepository implements SharedPrefContract {
private final SharedPreferences settingsSharedPref; private final SharedPreferences settingsSharedPref;
@Inject @Inject
SharedPrefRepository(@ApplicationContext Context context, @SharedPreferencesInfo String sharedName) { SharedPrefRepository(Context context, @Named("sharedPrefName") String sharedName) {
appSharedPref = context.getSharedPreferences(sharedName, Context.MODE_PRIVATE); appSharedPref = context.getSharedPreferences(sharedName, Context.MODE_PRIVATE);
settingsSharedPref = PreferenceManager.getDefaultSharedPreferences(context); settingsSharedPref = PreferenceManager.getDefaultSharedPreferences(context);
} }
@ -70,6 +69,11 @@ public class SharedPrefRepository implements SharedPrefContract {
return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_ATTENDANCE_PRESENT, false); return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_ATTENDANCE_PRESENT, false);
} }
@Override
public int getCurrentTheme() {
return Integer.parseInt(settingsSharedPref.getString(SettingsFragment.SHARED_KEY_THEME, "1"));
}
@Override @Override
public int getServicesInterval() { public int getServicesInterval() {
return Integer.parseInt(settingsSharedPref.getString(SettingsFragment.SHARED_KEY_SERVICES_INTERVAL, "60")); return Integer.parseInt(settingsSharedPref.getString(SettingsFragment.SHARED_KEY_SERVICES_INTERVAL, "60"));
@ -89,4 +93,10 @@ public class SharedPrefRepository implements SharedPrefContract {
public boolean isMobileDisable() { public boolean isMobileDisable() {
return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_SERVICES_MOBILE_DISABLED, false); return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_SERVICES_MOBILE_DISABLED, false);
} }
@Override
public void cleanSharedPref() {
appSharedPref.edit().clear().apply();
settingsSharedPref.edit().clear().apply();
}
} }

View File

@ -2,6 +2,8 @@ package io.github.wulkanowy.data.sync;
import android.content.Context; import android.content.Context;
import org.greenrobot.greendao.database.Database;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
@ -11,6 +13,7 @@ import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan; import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.VulcanException; import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.data.db.dao.entities.Account; import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.DaoMaster;
import io.github.wulkanowy.data.db.dao.entities.DaoSession; import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Diary; 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.DiaryDao;
@ -20,11 +23,10 @@ import io.github.wulkanowy.data.db.dao.entities.StudentDao;
import io.github.wulkanowy.data.db.dao.entities.Symbol; import io.github.wulkanowy.data.db.dao.entities.Symbol;
import io.github.wulkanowy.data.db.dao.entities.SymbolDao; import io.github.wulkanowy.data.db.dao.entities.SymbolDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract; 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.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.security.CryptoException; import io.github.wulkanowy.utils.security.CryptoException;
import io.github.wulkanowy.utils.security.Scrambler; import io.github.wulkanowy.utils.security.Scrambler;
import timber.log.Timber;
@Singleton @Singleton
public class AccountSync { public class AccountSync {
@ -39,7 +41,7 @@ public class AccountSync {
@Inject @Inject
AccountSync(DaoSession daoSession, SharedPrefContract sharedPref, AccountSync(DaoSession daoSession, SharedPrefContract sharedPref,
Vulcan vulcan, @ApplicationContext Context context) { Vulcan vulcan, Context context) {
this.daoSession = daoSession; this.daoSession = daoSession;
this.sharedPref = sharedPref; this.sharedPref = sharedPref;
this.vulcan = vulcan; this.vulcan = vulcan;
@ -49,6 +51,8 @@ public class AccountSync {
public void registerUser(String email, String password, String symbol) public void registerUser(String email, String password, String symbol)
throws VulcanException, IOException, CryptoException { throws VulcanException, IOException, CryptoException {
clearUserData();
vulcan.setCredentials(email, password, symbol, null, null, null); vulcan.setCredentials(email, password, symbol, null, null, null);
daoSession.getDatabase().beginTransaction(); daoSession.getDatabase().beginTransaction();
@ -69,7 +73,7 @@ public class AccountSync {
} }
private Account insertAccount(String email, String password) throws CryptoException { private Account insertAccount(String email, String password) throws CryptoException {
LogUtils.debug("Register account: " + email); Timber.d("Register account");
Account account = new Account() Account account = new Account()
.setEmail(email) .setEmail(email)
.setPassword(Scrambler.encrypt(email, password, context)); .setPassword(Scrambler.encrypt(email, password, context));
@ -78,10 +82,11 @@ public class AccountSync {
} }
private Symbol insertSymbol(Account account) throws VulcanException, IOException { private Symbol insertSymbol(Account account) throws VulcanException, IOException {
LogUtils.debug("Register symbol: " + vulcan.getSymbol()); String schoolId = vulcan.getStudentAndParent().getSchoolID();
Timber.d("Register symbol %s", vulcan.getSymbol());
Symbol symbol = new Symbol() Symbol symbol = new Symbol()
.setUserId(account.getId()) .setUserId(account.getId())
.setSchoolId(vulcan.getStudentAndParent().getSchoolID()) .setSchoolId(schoolId)
.setSymbol(vulcan.getSymbol()); .setSymbol(vulcan.getSymbol());
daoSession.getSymbolDao().insert(symbol); daoSession.getSymbolDao().insert(symbol);
@ -93,7 +98,7 @@ public class AccountSync {
vulcan.getStudentAndParent().getStudents(), vulcan.getStudentAndParent().getStudents(),
symbol.getId() symbol.getId()
); );
LogUtils.debug("Register students: " + studentList.size()); Timber.d("Register students %s", studentList.size());
daoSession.getStudentDao().insertInTx(studentList); daoSession.getStudentDao().insertInTx(studentList);
} }
@ -104,7 +109,7 @@ public class AccountSync {
StudentDao.Properties.SymbolId.eq(symbolEntity.getId()), StudentDao.Properties.SymbolId.eq(symbolEntity.getId()),
StudentDao.Properties.Current.eq(true) StudentDao.Properties.Current.eq(true)
).unique().getId()); ).unique().getId());
LogUtils.debug("Register diaries: " + diaryList.size()); Timber.d("Register diaries %s", diaryList.size());
daoSession.getDiaryDao().insertInTx(diaryList); daoSession.getDiaryDao().insertInTx(diaryList);
} }
@ -114,19 +119,19 @@ public class AccountSync {
daoSession.getDiaryDao().queryBuilder().where( daoSession.getDiaryDao().queryBuilder().where(
DiaryDao.Properties.Current.eq(true) DiaryDao.Properties.Current.eq(true)
).unique().getId()); ).unique().getId());
LogUtils.debug("Register semesters: " + semesterList.size()); Timber.d("Register semesters %s", semesterList.size());
daoSession.getSemesterDao().insertInTx(semesterList); daoSession.getSemesterDao().insertInTx(semesterList);
} }
public void initLastUser() throws IOException, CryptoException { public void initLastUser() throws CryptoException {
long userId = sharedPref.getCurrentUserId(); long userId = sharedPref.getCurrentUserId();
if (userId == 0) { if (userId == 0) {
throw new IOException("Can't find saved user"); 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); Account account = daoSession.getAccountDao().load(userId);
@ -152,4 +157,11 @@ public class AccountSync {
diary.getValue() diary.getValue()
); );
} }
private void clearUserData() {
Database database = daoSession.getDatabase();
DaoMaster.dropAllTables(database, true);
DaoMaster.createAllTables(database, true);
sharedPref.setCurrentUserId(0);
}
} }

View File

@ -1,7 +1,6 @@
package io.github.wulkanowy.data.sync; package io.github.wulkanowy.data.sync;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao; import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.utils.DataObjectConverter; import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
import io.github.wulkanowy.utils.TimeUtils;
@Singleton @Singleton
public class AttendanceSync { public class AttendanceSync {
@ -37,10 +35,10 @@ public class AttendanceSync {
this.vulcan = vulcan; 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; this.diaryId = diaryId;
io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> weekApi = getWeekFromApi(getNormalizedDate(date)); io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> weekApi = getWeekFromApi(date);
Week weekDb = getWeekFromDb(weekApi.getStartDayDate()); Week weekDb = getWeekFromDb(weekApi.getStartDayDate());
long weekId = updateWeekInDb(weekDb, weekApi); long weekId = updateWeekInDb(weekDb, weekApi);
@ -49,15 +47,11 @@ public class AttendanceSync {
daoSession.getAttendanceLessonDao().saveInTx(lessonList); daoSession.getAttendanceLessonDao().saveInTx(lessonList);
LogUtils.debug("Synchronization attendance lessons (amount = " + lessonList.size() + ")"); Timber.d("Attendance synchronization complete (%s)", lessonList.size());
}
private String getNormalizedDate(String date) throws ParseException {
return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : "";
} }
private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> getWeekFromApi(String date) private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> getWeekFromApi(String date)
throws IOException, ParseException, VulcanException { throws IOException, VulcanException {
return vulcan.getAttendanceTable().getWeekTable(date); return vulcan.getAttendanceTable().getWeekTable(date);
} }

View File

@ -1,7 +1,6 @@
package io.github.wulkanowy.data.sync; package io.github.wulkanowy.data.sync;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao; import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.utils.DataObjectConverter; import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
import io.github.wulkanowy.utils.TimeUtils;
public class ExamsSync { public class ExamsSync {
@ -35,11 +33,10 @@ public class ExamsSync {
this.vulcan = vulcan; this.vulcan = vulcan;
} }
public void syncExams(long diaryId, String date) throws IOException, VulcanException, public void syncExams(long diaryId, String date) throws IOException, VulcanException {
ParseException {
this.diaryId = diaryId; this.diaryId = diaryId;
io.github.wulkanowy.api.generic.Week<ExamDay> weekApi = getWeekFromApi(getNormalizedDate(date)); io.github.wulkanowy.api.generic.Week<ExamDay> weekApi = getWeekFromApi(date);
Week weekDb = getWeekFromDb(weekApi.getStartDayDate()); Week weekDb = getWeekFromDb(weekApi.getStartDayDate());
long weekId = updateWeekInDb(weekDb, weekApi); long weekId = updateWeekInDb(weekDb, weekApi);
@ -48,7 +45,7 @@ public class ExamsSync {
daoSession.getExamDao().saveInTx(examList); daoSession.getExamDao().saveInTx(examList);
LogUtils.debug("Synchronization exams (amount = " + examList.size() + ")"); Timber.d("Exams synchronization complete (%s)", examList.size());
} }
private Week getWeekFromDb(String date) { private Week getWeekFromDb(String date) {
@ -59,7 +56,7 @@ public class ExamsSync {
} }
private io.github.wulkanowy.api.generic.Week<ExamDay> getWeekFromApi(String date) private io.github.wulkanowy.api.generic.Week<ExamDay> getWeekFromApi(String date)
throws VulcanException, IOException, ParseException { throws VulcanException, IOException {
return vulcan.getExamsList().getWeek(date, true); return vulcan.getExamsList().getWeek(date, true);
} }
@ -77,10 +74,6 @@ public class ExamsSync {
return daoSession.getWeekDao().insert(weekApiEntity); 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) { private Day getDayFromDb(String date, long weekId) {
return daoSession.getDayDao().queryBuilder().where( return daoSession.getDayDao().queryBuilder().where(
DayDao.Properties.WeekId.eq(weekId), DayDao.Properties.WeekId.eq(weekId),
@ -135,6 +128,7 @@ public class ExamsSync {
.where(ExamDao.Properties.DayId.eq(dayId), .where(ExamDao.Properties.DayId.eq(dayId),
ExamDao.Properties.EntryDate.eq(examApi.getEntryDate()), ExamDao.Properties.EntryDate.eq(examApi.getEntryDate()),
ExamDao.Properties.SubjectAndGroup.eq(examApi.getSubjectAndGroup()), ExamDao.Properties.SubjectAndGroup.eq(examApi.getSubjectAndGroup()),
ExamDao.Properties.Type.eq(examApi.getType()),
ExamDao.Properties.Teacher.eq(examApi.getTeacher())) ExamDao.Properties.Teacher.eq(examApi.getTeacher()))
.unique(); .unique();
} }

View File

@ -1,7 +1,6 @@
package io.github.wulkanowy.data.sync; package io.github.wulkanowy.data.sync;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.data.db.dao.entities.SubjectDao;
import io.github.wulkanowy.utils.DataObjectConverter; import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.EntitiesCompare; import io.github.wulkanowy.utils.EntitiesCompare;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
@Singleton @Singleton
public class GradeSync { public class GradeSync {
@ -33,7 +32,7 @@ public class GradeSync {
this.vulcan = vulcan; this.vulcan = vulcan;
} }
public void sync(long semesterId) throws IOException, VulcanException, ParseException { public void sync(long semesterId) throws IOException, VulcanException {
this.semesterId = semesterId; this.semesterId = semesterId;
Semester semester = daoSession.getSemesterDao().load(semesterId); Semester semester = daoSession.getSemesterDao().load(semesterId);
@ -44,7 +43,7 @@ public class GradeSync {
daoSession.getGradeDao().deleteInTx(semester.getGradeList()); daoSession.getGradeDao().deleteInTx(semester.getGradeList());
daoSession.getGradeDao().insertInTx(lastList); daoSession.getGradeDao().insertInTx(lastList);
LogUtils.debug("Synchronization grades (amount = " + lastList.size() + ")"); Timber.d("Grades synchronization complete (%s)", lastList.size());
} }
private void resetSemesterRelations(Semester semester) { private void resetSemesterRelations(Semester semester) {
@ -64,7 +63,7 @@ public class GradeSync {
return updatedList; return updatedList;
} }
private List<Grade> getComparedList(Semester semester) throws IOException, VulcanException, ParseException { private List<Grade> getComparedList(Semester semester) throws IOException, VulcanException {
List<Grade> gradesFromNet = DataObjectConverter.gradesToGradeEntities( List<Grade> gradesFromNet = DataObjectConverter.gradesToGradeEntities(
vulcan.getGradesList().getAll(semester.getValue()), semesterId); vulcan.getGradesList().getAll(semester.getValue()), semesterId);

View File

@ -0,0 +1,8 @@
package io.github.wulkanowy.data.sync;
public class NotRegisteredUserException extends RuntimeException {
public NotRegisteredUserException(String message) {
super(message);
}
}

View File

@ -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.Semester;
import io.github.wulkanowy.data.db.dao.entities.Subject; import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.utils.DataObjectConverter; import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
@Singleton @Singleton
public class SubjectSync { public class SubjectSync {
@ -40,7 +40,7 @@ public class SubjectSync {
daoSession.getSubjectDao().deleteInTx(getSubjectsFromDb()); daoSession.getSubjectDao().deleteInTx(getSubjectsFromDb());
daoSession.getSubjectDao().insertInTx(lastList); daoSession.getSubjectDao().insertInTx(lastList);
LogUtils.debug("Synchronization subjects (amount = " + lastList.size() + ")"); Timber.d("Subjects synchronization complete (%s)", lastList.size());
} }
private List<Subject> getSubjectsFromNet(Semester semester) throws VulcanException, IOException { private List<Subject> getSubjectsFromNet(Semester semester) throws VulcanException, IOException {

View File

@ -1,7 +1,6 @@
package io.github.wulkanowy.data.sync; package io.github.wulkanowy.data.sync;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -47,17 +46,17 @@ public class SyncRepository implements SyncContract {
} }
@Override @Override
public void initLastUser() throws IOException, CryptoException { public void initLastUser() throws CryptoException {
accountSync.initLastUser(); accountSync.initLastUser();
} }
@Override @Override
public void syncGrades(int semesterName) throws VulcanException, IOException, ParseException { public void syncGrades(int semesterName) throws VulcanException, IOException {
gradeSync.sync(semesterName); gradeSync.sync(semesterName);
} }
@Override @Override
public void syncGrades() throws VulcanException, IOException, ParseException { public void syncGrades() throws VulcanException, IOException {
gradeSync.sync(database.getCurrentSemesterId()); gradeSync.sync(database.getCurrentSemesterId());
} }
@ -72,12 +71,12 @@ public class SyncRepository implements SyncContract {
} }
@Override @Override
public void syncAttendance() throws ParseException, IOException, VulcanException { public void syncAttendance() throws IOException, VulcanException {
attendanceSync.syncAttendance(database.getCurrentDiaryId(), null); attendanceSync.syncAttendance(database.getCurrentDiaryId(), null);
} }
@Override @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) { if (diaryId != 0) {
attendanceSync.syncAttendance(diaryId, date); attendanceSync.syncAttendance(diaryId, date);
} else { } else {
@ -86,12 +85,12 @@ public class SyncRepository implements SyncContract {
} }
@Override @Override
public void syncTimetable() throws VulcanException, IOException, ParseException { public void syncTimetable() throws VulcanException, IOException {
timetableSync.syncTimetable(database.getCurrentDiaryId(), null); timetableSync.syncTimetable(database.getCurrentDiaryId(), null);
} }
@Override @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) { if (diaryId != 0) {
timetableSync.syncTimetable(diaryId, date); timetableSync.syncTimetable(diaryId, date);
} else { } else {
@ -100,12 +99,12 @@ public class SyncRepository implements SyncContract {
} }
@Override @Override
public void syncExams() throws VulcanException, IOException, ParseException { public void syncExams() throws VulcanException, IOException {
examsSync.syncExams(database.getCurrentDiaryId(), null); examsSync.syncExams(database.getCurrentDiaryId(), null);
} }
@Override @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) { if (diaryId != 0) {
examsSync.syncExams(diaryId, date); examsSync.syncExams(diaryId, date);
} else { } else {
@ -114,7 +113,7 @@ public class SyncRepository implements SyncContract {
} }
@Override @Override
public void syncAll() throws VulcanException, IOException, ParseException { public void syncAll() throws VulcanException, IOException {
syncSubjects(); syncSubjects();
syncGrades(); syncGrades();
syncAttendance(); syncAttendance();

View File

@ -1,7 +1,8 @@
package io.github.wulkanowy.data.sync; package io.github.wulkanowy.data.sync;
import org.apache.commons.collections4.CollectionUtils;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -19,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.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao; import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.utils.DataObjectConverter; import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils; import timber.log.Timber;
import io.github.wulkanowy.utils.TimeUtils;
@Singleton @Singleton
public class TimetableSync { public class TimetableSync {
@ -37,10 +37,10 @@ public class TimetableSync {
this.vulcan = vulcan; 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; this.diaryId = diaryId;
io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.timetable.TimetableDay> weekApi = getWeekFromApi(getNormalizedDate(date)); io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.timetable.TimetableDay> weekApi = getWeekFromApi(date);
Week weekDb = getWeekFromDb(weekApi.getStartDayDate()); Week weekDb = getWeekFromDb(weekApi.getStartDayDate());
long weekId = updateWeekInDb(weekDb, weekApi); long weekId = updateWeekInDb(weekDb, weekApi);
@ -49,15 +49,11 @@ public class TimetableSync {
daoSession.getTimetableLessonDao().saveInTx(lessonList); daoSession.getTimetableLessonDao().saveInTx(lessonList);
LogUtils.debug("Synchronization timetable lessons (amount = " + lessonList.size() + ")"); Timber.d("Timetable synchronization complete (%s)", lessonList.size());
}
private String getNormalizedDate(String date) throws ParseException {
return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : "";
} }
private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.timetable.TimetableDay> getWeekFromApi(String date) private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.timetable.TimetableDay> getWeekFromApi(String date)
throws IOException, ParseException, VulcanException { throws IOException, VulcanException {
return vulcan.getTimetable().getWeekTable(date); return vulcan.getTimetable().getWeekTable(date);
} }
@ -125,6 +121,16 @@ public class TimetableSync {
List<TimetableLesson> lessonsFromApiEntities = DataObjectConverter List<TimetableLesson> lessonsFromApiEntities = DataObjectConverter
.lessonsToTimetableLessonsEntities(lessons); .lessonsToTimetableLessonsEntities(lessons);
List<TimetableLesson> lessonsFromDbEntities = getLessonsFromDb(dayId);
if (!lessonsFromDbEntities.isEmpty()) {
List<TimetableLesson> lessonToRemove = new ArrayList<>(CollectionUtils.removeAll(lessonsFromDbEntities, lessonsFromApiEntities));
for (TimetableLesson timetableLesson : lessonToRemove) {
daoSession.getTimetableLessonDao().delete(timetableLesson);
}
}
for (TimetableLesson apiLessonEntity : lessonsFromApiEntities) { for (TimetableLesson apiLessonEntity : lessonsFromApiEntities) {
TimetableLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId); TimetableLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId);
@ -148,4 +154,8 @@ public class TimetableSync {
TimetableLessonDao.Properties.EndTime.eq(apiEntity.getEndTime())) TimetableLessonDao.Properties.EndTime.eq(apiEntity.getEndTime()))
.unique(); .unique();
} }
private List<TimetableLesson> getLessonsFromDb(long dayId) {
return daoSession.getDayDao().load(dayId).getTimetableLessons();
}
} }

View File

@ -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<WulkanowyApp> {
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<WulkanowyApp> {
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<AttendanceHeaderItem> provideAttendanceTabAdapter() {
return new FlexibleAdapter<>(null);
}
@Provides
FlexibleAdapter<TimetableHeaderItem> provideTimetableTabAdapter() {
return new FlexibleAdapter<>(null);
}
@Provides
FlexibleAdapter<GradeHeaderItem> provideGradesAdapter() {
return new FlexibleAdapter<>(null);
}
@Provides
FlexibleAdapter<ExamsSubItem> 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;
}
}

View File

@ -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.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;

View File

@ -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.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier; import javax.inject.Scope;
@Qualifier @Scope
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface DatabaseInfo { public @interface PerChildFragment {
} }

View File

@ -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.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;

View File

@ -4,7 +4,6 @@ import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import com.crashlytics.android.Crashlytics;
import com.firebase.jobdispatcher.Constraint; import com.firebase.jobdispatcher.Constraint;
import com.firebase.jobdispatcher.FirebaseJobDispatcher; import com.firebase.jobdispatcher.FirebaseJobDispatcher;
import com.firebase.jobdispatcher.GooglePlayDriver; import com.firebase.jobdispatcher.GooglePlayDriver;
@ -20,13 +19,16 @@ import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.android.AndroidInjection;
import io.github.wulkanowy.R; 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.RepositoryContract;
import io.github.wulkanowy.data.db.dao.entities.Grade; 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.services.notifies.GradeNotify;
import io.github.wulkanowy.ui.main.MainActivity; import io.github.wulkanowy.ui.main.MainActivity;
import io.github.wulkanowy.utils.LogUtils; import io.github.wulkanowy.utils.FabricUtils;
import timber.log.Timber;
public class SyncJob extends SimpleJobService { public class SyncJob extends SimpleJobService {
@ -59,7 +61,7 @@ public class SyncJob extends SimpleJobService {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
((WulkanowyApp) getApplication()).getApplicationComponent().inject(this); AndroidInjection.inject(this);
} }
@Override @Override
@ -73,10 +75,24 @@ public class SyncJob extends SimpleJobService {
if (!gradeList.isEmpty() && repository.getSharedRepo().isNotifyEnable()) { if (!gradeList.isEmpty() && repository.getSharedRepo().isNotifyEnable()) {
showNotification(); showNotification();
} }
FabricUtils.logLogin("Background", true);
return JobService.RESULT_SUCCESS; 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) { } catch (Exception e) {
Crashlytics.logException(e); logError(e);
LogUtils.error("During background synchronization an error occurred", e);
return JobService.RESULT_FAIL_RETRY; return JobService.RESULT_FAIL_RETRY;
} }
} }
@ -116,4 +132,9 @@ public class SyncJob extends SimpleJobService {
gradeList.size(), gradeList.size()); gradeList.size(), gradeList.size());
} }
} }
private void logError(Exception e) {
FabricUtils.logLogin("Background", false);
Timber.e(e, "During background synchronization an error occurred");
}
} }

View File

@ -3,12 +3,20 @@ package io.github.wulkanowy.services.widgets;
import android.content.Intent; import android.content.Intent;
import android.widget.RemoteViewsService; 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; import io.github.wulkanowy.ui.widgets.TimetableWidgetFactory;
public class TimetableWidgetServices extends RemoteViewsService { public class TimetableWidgetServices extends RemoteViewsService {
@Inject
RepositoryContract repository;
@Override @Override
public RemoteViewsFactory onGetViewFactory(Intent intent) { public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new TimetableWidgetFactory(getApplicationContext()); AndroidInjection.inject(this);
return new TimetableWidgetFactory(getApplicationContext(), repository);
} }
} }

View File

@ -1,35 +1,51 @@
package io.github.wulkanowy.ui.base; package io.github.wulkanowy.ui.base;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; 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.support.v7.app.AppCompatDelegate;
import android.widget.Toast; import android.view.View;
import butterknife.ButterKnife;
import butterknife.Unbinder; import butterknife.Unbinder;
import dagger.android.support.DaggerAppCompatActivity;
import io.github.wulkanowy.R; 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; import io.github.wulkanowy.utils.NetworkUtils;
public abstract class BaseActivity extends AppCompatActivity implements BaseContract.View { public abstract class BaseActivity extends DaggerAppCompatActivity implements BaseContract.View {
private ActivityComponent activityComponent;
private Unbinder unbinder; private Unbinder unbinder;
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
activityComponent = DaggerActivityComponent.builder() protected void injectViews() {
.activityModule(new ActivityModule(this)) unbinder = ButterKnife.bind(this);
.applicationComponent(((WulkanowyApp) getApplication()).getApplicationComponent()) }
.build();
@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 @Override
@ -38,33 +54,6 @@ public abstract class BaseActivity extends AppCompatActivity implements BaseCont
if (unbinder != null) { if (unbinder != null) {
unbinder.unbind(); unbinder.unbind();
} }
} invalidateOptionsMenu();
@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;
} }
} }

View File

@ -1,27 +1,22 @@
package io.github.wulkanowy.ui.base; package io.github.wulkanowy.ui.base;
import android.support.annotation.StringRes; import android.support.annotation.NonNull;
import io.github.wulkanowy.di.annotations.PerActivity;
public interface BaseContract { public interface BaseContract {
interface View { interface View {
void onError(@StringRes int resId); void showMessage(@NonNull String text);
void onError(String message); void showNoNetworkMessage();
void onNoNetworkError();
boolean isNetworkConnected(); boolean isNetworkConnected();
} }
@PerActivity
interface Presenter<V extends View> { interface Presenter<V extends View> {
void onStart(V view); void attachView(@NonNull V view);
void onDestroy(); void detachView();
} }
} }

View File

@ -1,56 +1,20 @@
package io.github.wulkanowy.ui.base; package io.github.wulkanowy.ui.base;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.view.View; import android.view.View;
import butterknife.ButterKnife;
import butterknife.Unbinder; import butterknife.Unbinder;
import io.github.wulkanowy.R; import dagger.android.support.DaggerFragment;
import io.github.wulkanowy.WulkanowyApp; import io.github.wulkanowy.utils.NetworkUtils;
import io.github.wulkanowy.di.component.DaggerFragmentComponent;
import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.di.modules.FragmentModule;
public abstract class BaseFragment extends Fragment implements BaseContract.View { public abstract class BaseFragment extends DaggerFragment implements BaseContract.View {
private BaseActivity activity;
private Unbinder unbinder; private Unbinder unbinder;
private FragmentComponent fragmentComponent; protected void injectViews(@NonNull View view) {
unbinder = ButterKnife.bind(this, view);
@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();
} }
@Override @Override
@ -61,44 +25,32 @@ public abstract class BaseFragment extends Fragment implements BaseContract.View
super.onDestroyView(); super.onDestroyView();
} }
@Override public void setTitle(String title) {
public void onError(int resId) { if (getActivity() != null) {
onError(getString(resId)); getActivity().setTitle(title);
}
@Override
public void onError(String message) {
if (activity != null) {
activity.onError(message);
} }
} }
@Override @Override
public void onNoNetworkError() { public void showMessage(@NonNull String text) {
onError(R.string.noInternet_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 @Override
public boolean isNetworkConnected() { public boolean isNetworkConnected() {
return activity != null && activity.isNetworkConnected(); return NetworkUtils.isOnline(getContext());
}
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
} }
} }

View File

@ -1,5 +1,7 @@
package io.github.wulkanowy.ui.base; package io.github.wulkanowy.ui.base;
import android.support.annotation.NonNull;
import javax.inject.Inject; import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.data.RepositoryContract;
@ -16,12 +18,12 @@ public class BasePresenter<V extends BaseContract.View> implements BaseContract.
} }
@Override @Override
public void onStart(V view) { public void attachView(@NonNull V view) {
this.view = view; this.view = view;
} }
@Override @Override
public void onDestroy() { public void detachView() {
view = null; view = null;
} }

View File

@ -5,7 +5,7 @@ import android.animation.AnimatorListenerAdapter;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.Snackbar; import android.support.annotation.NonNull;
import android.support.design.widget.TextInputLayout; import android.support.design.widget.TextInputLayout;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.view.View; import android.view.View;
@ -14,11 +14,11 @@ import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import javax.inject.Inject; import javax.inject.Inject;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick; import butterknife.OnClick;
import butterknife.OnEditorAction; import butterknife.OnEditorAction;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
@ -64,14 +64,10 @@ public class LoginActivity extends BaseActivity implements LoginContract.View {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login); setContentView(R.layout.activity_login);
injectViews();
setButterKnife(ButterKnife.bind(this)); presenter.attachView(this);
getActivityComponent().inject(this);
presenter.onStart(this);
setUpOnCreate(); setUpOnCreate();
} }
protected void setUpOnCreate() { protected void setUpOnCreate() {
@ -165,12 +161,6 @@ public class LoginActivity extends BaseActivity implements LoginContract.View {
KeyboardUtils.hideSoftInput(this); KeyboardUtils.hideSoftInput(this);
} }
@Override
public void onError(String message) {
Snackbar.make(findViewById(R.id.login_activity_container), message,
Snackbar.LENGTH_LONG).show();
}
@Override @Override
public void setStepOneLoginProgress() { public void setStepOneLoginProgress() {
onLoginProgressUpdate("1", getString(R.string.step_login)); onLoginProgressUpdate("1", getString(R.string.step_login));
@ -222,13 +212,25 @@ public class LoginActivity extends BaseActivity implements LoginContract.View {
} }
} }
@NonNull
@Override @Override
public void onDestroy() { protected View getMessageView() {
super.onDestroy(); return findViewById(R.id.login_activity_container);
presenter.onDestroy(); }
@Override
public void onSyncFailed() {
Toast.makeText(getApplicationContext(), R.string.login_sync_error, Toast.LENGTH_LONG).show();
} }
private void onLoginProgressUpdate(String step, String message) { private void onLoginProgressUpdate(String step, String message) {
loginProgressText.setText(String.format("%1$s/2 - %2$s...", step, message)); loginProgressText.setText(String.format("%1$s/2 - %2$s...", step, message));
} }
@Override
public void onDestroy() {
presenter.detachView();
super.onDestroy();
}
} }

View File

@ -1,7 +1,5 @@
package io.github.wulkanowy.ui.login; 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; import io.github.wulkanowy.ui.base.BaseContract;
public interface LoginContract { public interface LoginContract {
@ -35,9 +33,10 @@ public interface LoginContract {
void showActionBar(boolean show); void showActionBar(boolean show);
void onSyncFailed();
} }
@PerActivity
interface Presenter extends BaseContract.Presenter<View> { interface Presenter extends BaseContract.Presenter<View> {
void attemptLogin(String email, String password, String symbol); void attemptLogin(String email, String password, String symbol);
@ -48,10 +47,8 @@ public interface LoginContract {
void onLoginProgress(int step); void onLoginProgress(int step);
void onEndAsync(boolean success, Exception exception); void onEndAsync(int success, Exception exception);
void onCanceledAsync(); void onCanceledAsync();
RepositoryContract getRepository();
} }
} }

View File

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

View File

@ -11,6 +11,7 @@ import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.ui.base.BasePresenter; import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.utils.AppConstant; import io.github.wulkanowy.utils.AppConstant;
import io.github.wulkanowy.utils.FabricUtils;
public class LoginPresenter extends BasePresenter<LoginContract.View> public class LoginPresenter extends BasePresenter<LoginContract.View>
implements LoginContract.Presenter { implements LoginContract.Presenter {
@ -46,7 +47,7 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
loginAsync.execute(); loginAsync.execute();
} else { } else {
getView().onNoNetworkError(); getView().showNoNetworkMessage();
} }
getView().hideSoftInput(); getView().hideSoftInput();
@ -82,20 +83,30 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
} }
@Override @Override
public void onEndAsync(boolean success, Exception exception) { public void onEndAsync(int success, Exception exception) {
if (success) { switch (success) {
getView().openMainActivity(); case LoginTask.LOGIN_AND_SYNC_SUCCESS:
return; FabricUtils.logRegister(true, getRepository().getDbRepo().getCurrentSymbol().getSymbol(), "Success");
} else if (exception instanceof BadCredentialsException) { getView().openMainActivity();
getView().setErrorPassIncorrect(); return;
getView().showSoftInput(); case LoginTask.SYNC_FAILED:
} else if (exception instanceof AccountPermissionException) { FabricUtils.logRegister(true, symbol, exception.getMessage());
getView().setErrorSymbolRequired(); getView().onSyncFailed();
getView().showSoftInput(); getView().openMainActivity();
} else { return;
getView().onError(getRepository().getResRepo().getErrorLoginMessage(exception)); 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().showActionBar(true);
getView().showLoginProgress(false); getView().showLoginProgress(false);
} }
@ -157,11 +168,11 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
} }
@Override @Override
public void onDestroy() { public void detachView() {
if (loginAsync != null) { if (loginAsync != null) {
loginAsync.cancel(true); loginAsync.cancel(true);
loginAsync = null; loginAsync = null;
} }
super.onDestroy(); super.detachView();
} }
} }

View File

@ -2,7 +2,13 @@ package io.github.wulkanowy.ui.login;
import android.os.AsyncTask; import android.os.AsyncTask;
public class LoginTask extends AsyncTask<Void, Integer, Boolean> { public class LoginTask extends AsyncTask<Void, Integer, Integer> {
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; private LoginContract.Presenter presenter;
@ -18,18 +24,23 @@ public class LoginTask extends AsyncTask<Void, Integer, Boolean> {
} }
@Override @Override
protected Boolean doInBackground(Void... params) { protected Integer doInBackground(Void... params) {
try { try {
publishProgress(1); publishProgress(1);
presenter.onDoInBackground(1); presenter.onDoInBackground(1);
} catch (Exception e) {
exception = e;
return LOGIN_FAILED;
}
try {
publishProgress(2); publishProgress(2);
presenter.onDoInBackground(2); presenter.onDoInBackground(2);
} catch (Exception e) { } catch (Exception e) {
exception = e; exception = e;
return false; return SYNC_FAILED;
} }
return true; return LOGIN_AND_SYNC_SUCCESS;
} }
@Override @Override
@ -38,7 +49,7 @@ public class LoginTask extends AsyncTask<Void, Integer, Boolean> {
} }
@Override @Override
protected void onPostExecute(Boolean success) { protected void onPostExecute(Integer success) {
presenter.onEndAsync(success, exception); presenter.onEndAsync(success, exception);
} }

View File

@ -2,8 +2,9 @@ package io.github.wulkanowy.ui.main;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle; 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.app.ActionBar;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.view.View; import android.view.View;
@ -13,10 +14,11 @@ import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
import com.aurelhubert.ahbottomnavigation.AHBottomNavigationViewPager; import com.aurelhubert.ahbottomnavigation.AHBottomNavigationViewPager;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.services.jobs.SyncJob; import io.github.wulkanowy.services.jobs.SyncJob;
import io.github.wulkanowy.ui.base.BaseActivity; import io.github.wulkanowy.ui.base.BaseActivity;
import io.github.wulkanowy.ui.base.BasePagerAdapter; 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.grades.GradesFragment;
import io.github.wulkanowy.ui.main.settings.SettingsFragment; import io.github.wulkanowy.ui.main.settings.SettingsFragment;
import io.github.wulkanowy.ui.main.timetable.TimetableFragment; import io.github.wulkanowy.ui.main.timetable.TimetableFragment;
import io.github.wulkanowy.utils.CommonUtils;
public class MainActivity extends BaseActivity implements MainContract.View, public class MainActivity extends BaseActivity implements MainContract.View,
AHBottomNavigation.OnTabSelectedListener, OnFragmentIsReadyListener { AHBottomNavigation.OnTabSelectedListener, OnFragmentIsReadyListener {
@ -40,12 +43,19 @@ public class MainActivity extends BaseActivity implements MainContract.View,
@BindView(R.id.main_activity_progress_bar) @BindView(R.id.main_activity_progress_bar)
View progressBar; View progressBar;
@BindView(R.id.main_activity_appbar)
AppBarLayout appBar;
@Named("Main")
@Inject @Inject
BasePagerAdapter pagerAdapter; BasePagerAdapter pagerAdapter;
@Inject @Inject
MainContract.Presenter presenter; MainContract.Presenter presenter;
@Inject
RepositoryContract repository;
public static Intent getStartIntent(Context context) { public static Intent getStartIntent(Context context) {
return new Intent(context, MainActivity.class); return new Intent(context, MainActivity.class);
} }
@ -54,12 +64,10 @@ public class MainActivity extends BaseActivity implements MainContract.View,
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); setSupportActionBar((Toolbar) findViewById(R.id.main_activity_toolbar));
injectViews();
getActivityComponent().inject(this); presenter.attachView(this, getIntent().getIntExtra(EXTRA_CARD_ID_KEY, -1));
setButterKnife(ButterKnife.bind(this));
presenter.onStart(this, getIntent().getIntExtra(EXTRA_CARD_ID_KEY, -1));
} }
@Override @Override
@ -88,6 +96,8 @@ public class MainActivity extends BaseActivity implements MainContract.View,
@Override @Override
public boolean onTabSelected(int position, boolean wasSelected) { public boolean onTabSelected(int position, boolean wasSelected) {
presenter.onTabSelected(position, wasSelected); presenter.onTabSelected(position, wasSelected);
appBar.setExpanded(true, true);
invalidateOptionsMenu();
return true; return true;
} }
@ -119,8 +129,8 @@ public class MainActivity extends BaseActivity implements MainContract.View,
R.drawable.ic_menu_other_24dp)); R.drawable.ic_menu_other_24dp));
bottomNavigation.setAccentColor(getResources().getColor(R.color.colorPrimary)); bottomNavigation.setAccentColor(getResources().getColor(R.color.colorPrimary));
bottomNavigation.setInactiveColor(Color.BLACK); bottomNavigation.setInactiveColor(CommonUtils.getThemeAttrColor(this, android.R.attr.textColorTertiary));
bottomNavigation.setBackgroundColor(getResources().getColor(R.color.colorBackgroundBottomNav)); bottomNavigation.setDefaultBackgroundColor(CommonUtils.getThemeAttrColor(this, R.attr.bottomNavBackground));
bottomNavigation.setTitleState(AHBottomNavigation.TitleState.ALWAYS_SHOW); bottomNavigation.setTitleState(AHBottomNavigation.TitleState.ALWAYS_SHOW);
bottomNavigation.setOnTabSelectedListener(this); bottomNavigation.setOnTabSelectedListener(this);
bottomNavigation.setCurrentItem(tabPosition); bottomNavigation.setCurrentItem(tabPosition);
@ -146,9 +156,15 @@ public class MainActivity extends BaseActivity implements MainContract.View,
SyncJob.start(getApplicationContext(), interval, useOnlyWifi); SyncJob.start(getApplicationContext(), interval, useOnlyWifi);
} }
@NonNull
@Override
protected View getMessageView() {
return findViewById(R.id.main_activity_view_pager);
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
presenter.detachView();
super.onDestroy(); super.onDestroy();
presenter.onDestroy();
} }
} }

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.main; 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; import io.github.wulkanowy.ui.base.BaseContract;
public interface MainContract { public interface MainContract {
@ -22,10 +23,9 @@ public interface MainContract {
void startSyncService(int interval, boolean useOnlyWifi); void startSyncService(int interval, boolean useOnlyWifi);
} }
@PerActivity
interface Presenter extends BaseContract.Presenter<View> { interface Presenter extends BaseContract.Presenter<View> {
void onStart(View view, int tabPositionIntent); void attachView(@NonNull View view, int initTabId);
void onTabSelected(int position, boolean wasSelected); void onTabSelected(int position, boolean wasSelected);

View File

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

View File

@ -1,6 +1,8 @@
package io.github.wulkanowy.ui.main; package io.github.wulkanowy.ui.main;
import android.support.annotation.NonNull;
import javax.inject.Inject; import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract; import io.github.wulkanowy.data.RepositoryContract;
@ -17,15 +19,15 @@ public class MainPresenter extends BasePresenter<MainContract.View>
} }
@Override @Override
public void onStart(MainContract.View view, int tabPositionIntent) { public void attachView(@NonNull MainContract.View view, int initTabId) {
super.onStart(view); super.attachView(view);
getView().showProgressBar(true); getView().showProgressBar(true);
getView().hideActionBar(); getView().hideActionBar();
int tabPosition; int tabPosition;
if (tabPositionIntent != -1) { if (initTabId != -1) {
tabPosition = tabPositionIntent; tabPosition = initTabId;
} else { } else {
tabPosition = getRepository().getSharedRepo().getStartupTab(); tabPosition = getRepository().getSharedRepo().getStartupTab();
} }

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.main.attendance; 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.base.BaseContract;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
@ -21,12 +22,11 @@ public interface AttendanceContract {
void setThemeForTab(int position); void setThemeForTab(int position);
} }
@PerActivity
interface Presenter extends BaseContract.Presenter<View> { interface Presenter extends BaseContract.Presenter<View> {
void onFragmentActivated(boolean isVisible); void onFragmentActivated(boolean isVisible);
void onStart(View view, OnFragmentIsReadyListener listener); void attachView(@NonNull View view, OnFragmentIsReadyListener listener);
void setRestoredPosition(int position); void setRestoredPosition(int position);
} }

View File

@ -78,7 +78,7 @@ public class AttendanceDialogFragment extends DialogFragment {
description.setText(lesson.getDescription()); description.setText(lesson.getDescription());
if (lesson.getAbsenceUnexcused()) { if (lesson.getAbsenceUnexcused()) {
description.setTextColor(getResources().getColor(R.color.colorPrimaryDark)); description.setTextColor(getResources().getColor(R.color.colorPrimary));
} }
return view; return view;

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.main.attendance;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout; import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -11,14 +10,14 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife;
import io.github.wulkanowy.R; 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.BaseFragment;
import io.github.wulkanowy.ui.base.BasePagerAdapter; import io.github.wulkanowy.ui.base.BasePagerAdapter;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.attendance.tab.AttendanceTabFragment;
public class AttendanceFragment extends BaseFragment implements AttendanceContract.View { public class AttendanceFragment extends BaseFragment implements AttendanceContract.View {
@ -31,6 +30,7 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
TabLayout tabLayout; TabLayout tabLayout;
@Inject @Inject
@Named("Attendance")
BasePagerAdapter pagerAdapter; BasePagerAdapter pagerAdapter;
@Inject @Inject
@ -41,18 +41,13 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_attendance, container, false); View view = inflater.inflate(R.layout.fragment_attendance, container, false);
injectViews(view);
FragmentComponent component = getFragmentComponent(); presenter.attachView(this, (OnFragmentIsReadyListener) getActivity());
if (component != null) {
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
presenter.onStart(this, (OnFragmentIsReadyListener) getActivity());
if (savedInstanceState != null) { if (savedInstanceState != null) {
presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY)); presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY));
}
} }
return view; return view;
} }
@ -93,14 +88,6 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
setTitle(getString(R.string.attendance_text)); setTitle(getString(R.string.attendance_text));
} }
@Override
public void onError(String message) {
if (getActivity() != null) {
Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager),
message, Snackbar.LENGTH_LONG).show();
}
}
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem()); outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem());
@ -109,7 +96,7 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
@Override @Override
public void onDestroyView() { public void onDestroyView() {
presenter.onDestroy(); presenter.detachView();
super.onDestroyView(); super.onDestroyView();
} }
} }

View File

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

View File

@ -1,5 +1,7 @@
package io.github.wulkanowy.ui.main.attendance; package io.github.wulkanowy.ui.main.attendance;
import android.support.annotation.NonNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -31,8 +33,8 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
} }
@Override @Override
public void onStart(AttendanceContract.View view, OnFragmentIsReadyListener listener) { public void attachView(@NonNull AttendanceContract.View view, OnFragmentIsReadyListener listener) {
super.onStart(view); super.attachView(view);
this.listener = listener; this.listener = listener;
if (getView().isMenuVisible()) { if (getView().isMenuVisible()) {
@ -64,7 +66,7 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
} }
@Override @Override
public void onDoInBackgroundLoading() throws Exception { public void onDoInBackgroundLoading() {
for (String date : dates) { for (String date : dates) {
getView().setTabDataToAdapter(date); getView().setTabDataToAdapter(date);
} }
@ -92,14 +94,13 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
} }
@Override @Override
public void onDestroy() { public void detachView() {
isFirstSight = false; isFirstSight = false;
if (loadingTask != null) { if (loadingTask != null) {
loadingTask.cancel(true); loadingTask.cancel(true);
loadingTask = null; loadingTask = null;
} }
super.detachView();
super.onDestroy();
} }
} }

View File

@ -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.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
@ -14,7 +14,6 @@ import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.util.List; import java.util.List;
import butterknife.BindColor;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.FlexibleAdapter;
@ -23,13 +22,14 @@ import eu.davidea.flexibleadapter.items.IFlexible;
import eu.davidea.viewholders.ExpandableViewHolder; import eu.davidea.viewholders.ExpandableViewHolder;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.Day; import io.github.wulkanowy.data.db.dao.entities.Day;
import io.github.wulkanowy.utils.CommonUtils;
public class AttendanceHeaderItem public class AttendanceHeader
extends AbstractExpandableHeaderItem<AttendanceHeaderItem.HeaderViewHolder, AttendanceSubItem> { extends AbstractExpandableHeaderItem<AttendanceHeader.HeaderViewHolder, AttendanceSubItem> {
private Day day; private Day day;
AttendanceHeaderItem(Day day) { AttendanceHeader(Day day) {
this.day = day; this.day = day;
} }
@ -39,7 +39,7 @@ public class AttendanceHeaderItem
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
AttendanceHeaderItem that = (AttendanceHeaderItem) o; AttendanceHeader that = (AttendanceHeader) o;
return new EqualsBuilder() return new EqualsBuilder()
.append(day, that.day) .append(day, that.day)
@ -86,15 +86,6 @@ public class AttendanceHeaderItem
@BindView(R.id.attendance_header_free_name) @BindView(R.id.attendance_header_free_name)
TextView freeName; 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; private Context context;
HeaderViewHolder(View view, FlexibleAdapter adapter) { HeaderViewHolder(View view, FlexibleAdapter adapter) {
@ -117,16 +108,15 @@ public class AttendanceHeaderItem
setInactiveHeader(item.getAttendanceLessons().isEmpty()); setInactiveHeader(item.getAttendanceLessons().isEmpty());
} }
private void setInactiveHeader(boolean inactive) { private void setInactiveHeader(boolean inactive) {
((FrameLayout) getContentView()).setForeground(inactive ? null : getSelectableDrawable()); ((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) { if (inactive) {
getContentView().setBackgroundColor(backgroundFreeDay); getContentView().setBackgroundColor(CommonUtils.getThemeAttrColor(context, R.attr.colorControlHighlight));
} else { } else {
getContentView().setBackgroundDrawable(context.getResources() getContentView().setBackgroundDrawable(context.getResources().getDrawable(R.drawable.ic_border));
.getDrawable(R.drawable.ic_border));
} }
} }

View File

@ -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.Context;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
@ -20,13 +20,14 @@ import eu.davidea.flexibleadapter.items.IFlexible;
import eu.davidea.viewholders.FlexibleViewHolder; import eu.davidea.viewholders.FlexibleViewHolder;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.ui.main.attendance.AttendanceDialogFragment;
class AttendanceSubItem class AttendanceSubItem
extends AbstractSectionableItem<AttendanceSubItem.SubItemViewHolder, AttendanceHeaderItem> { extends AbstractSectionableItem<AttendanceSubItem.SubItemViewHolder, AttendanceHeader> {
private AttendanceLesson lesson; private AttendanceLesson lesson;
AttendanceSubItem(AttendanceHeaderItem header, AttendanceLesson lesson) { AttendanceSubItem(AttendanceHeader header, AttendanceLesson lesson) {
super(header); super(header);
this.lesson = lesson; this.lesson = lesson;
} }

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.main.attendance; package io.github.wulkanowy.ui.main.attendance.tab;
import java.util.List; import java.util.List;
@ -8,7 +8,7 @@ public interface AttendanceTabContract {
interface View extends BaseContract.View { interface View extends BaseContract.View {
void updateAdapterList(List<AttendanceHeaderItem> headerItems); void updateAdapterList(List<AttendanceHeader> headerItems);
void onRefreshSuccess(); void onRefreshSuccess();

View File

@ -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.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.widget.SwipeRefreshLayout; import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -15,11 +14,9 @@ import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
import io.github.wulkanowy.R; 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.BaseFragment;
public class AttendanceTabFragment extends BaseFragment implements AttendanceTabContract.View, public class AttendanceTabFragment extends BaseFragment implements AttendanceTabContract.View,
@ -43,7 +40,7 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
AttendanceTabContract.Presenter presenter; AttendanceTabContract.Presenter presenter;
@Inject @Inject
FlexibleAdapter<AttendanceHeaderItem> adapter; FlexibleAdapter<AttendanceHeader> adapter;
private boolean isFragmentVisible = false; private boolean isFragmentVisible = false;
@ -61,38 +58,32 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_attendance_tab, container, false); View view = inflater.inflate(R.layout.fragment_attendance_tab, container, false);
injectViews(view);
FragmentComponent component = getFragmentComponent(); if (getArguments() != null) {
if (component != null) { presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
if (getArguments() != null) {
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
}
presenter.onStart(this);
presenter.onFragmentActivated(isFragmentVisible);
} }
presenter.attachView(this);
presenter.onFragmentActivated(isFragmentVisible);
return view; return view;
} }
@Override @Override
protected void setUpOnViewCreated(View fragmentView) { public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
adapter.setAutoCollapseOnExpand(true); adapter.setAutoCollapseOnExpand(true);
adapter.setAutoScrollOnExpand(true); adapter.setAutoScrollOnExpand(true);
adapter.expandItemsAtStartUp(); adapter.expandItemsAtStartUp();
recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext())); recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext()));
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
refreshLayout.setColorSchemeResources(android.R.color.black); refreshLayout.setColorSchemeResources(android.R.color.black);
refreshLayout.setOnRefreshListener(this); refreshLayout.setOnRefreshListener(this);
} }
@Override @Override
public void updateAdapterList(List<AttendanceHeaderItem> headerItems) { public void updateAdapterList(List<AttendanceHeader> headerItems) {
adapter.updateDataSet(headerItems); adapter.updateDataSet(headerItems);
} }
@ -112,7 +103,7 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
@Override @Override
public void onRefreshSuccess() { public void onRefreshSuccess() {
onError(R.string.sync_completed); showMessage(R.string.sync_completed);
} }
@Override @Override
@ -130,17 +121,9 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); 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 @Override
public void onDestroyView() { public void onDestroyView() {
presenter.onDestroy(); presenter.detachView();
super.onDestroyView(); super.onDestroyView();
} }
} }

View File

@ -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<AttendanceHeader> provideAdapter() {
return new FlexibleAdapter<>(null);
}
}

Some files were not shown because too many files have changed in this diff Show More