1
0

Compare commits

..

26 Commits
0.4.5 ... 0.5.4

Author SHA1 Message Date
78849f0280 Version 0.5.4 2018-11-17 14:12:23 +01:00
78add470ba Provide back-compatibility in school discover 2018-11-17 14:02:40 +01:00
b39b9f8bba Fix tests 2018-11-16 02:02:24 +01:00
a50dfb3ea7 Fix snp app link after uonet upgrade 2018-11-16 01:48:11 +01:00
5dcd4f9b72 Version 0.5.2 2018-07-15 19:09:31 +02:00
c4792289ef Fix 29 migration (#146) 2018-07-15 19:02:49 +02:00
4b93a76693 Version 0.5.1 2018-07-15 15:40:07 +02:00
d44a1dbce1 Add weight on grades list (#145) 2018-07-15 15:15:19 +02:00
083765b020 Adjust colors of grades in dark theme (#143) 2018-07-15 14:20:22 +02:00
b8a31c3faf Upgrade android SDK (#144) 2018-07-14 20:09:58 +02:00
378aba9716 Add support for multiple SnP sites (#140) 2018-07-14 16:52:09 +02:00
5a4b8b22f3 Stop synchronization on holidays (#142) 2018-07-14 14:06:57 +02:00
8725640168 User short grade names in grades summary (#141) 2018-06-29 11:27:18 +02:00
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
180 changed files with 3044 additions and 1281 deletions

View File

@ -7,7 +7,7 @@ references:
container_config: &container_config
docker:
- image: circleci/android:api-26-alpha
- image: circleci/android:api-27-alpha
working_directory: *workspace_root
environment:
environment:
@ -87,11 +87,8 @@ jobs:
name: Upload unit code coverage to codecov
command: bash <(curl -s https://codecov.io/bash) -F app
- store_artifacts:
path: ./app/build/reports/tests/
destination: tests_reports/
- store_artifacts:
path: ./app/build/reports/jacoco/jacocoTestDebugUnitTestReport/
destination: coverage_reports/
path: ./app/build/reports/
destination: reports/
- store_test_results:
path: ./app/build/test-results
- persist_to_workspace:
@ -112,11 +109,8 @@ jobs:
name: Upload code coverage to codecov
command: bash <(curl -s https://codecov.io/bash) -F api
- store_artifacts:
path: ./api/build/reports/tests/
destination: tests_reports/
- store_artifacts:
path: ./api/build/reports/jacoco/test/
destination: coverage_reports/
path: ./api/build/reports/
destination: reports/
- store_test_results:
path: ./api/build/test-results
- persist_to_workspace:
@ -130,7 +124,7 @@ jobs:
- *attach_workspace
- run:
name: Setup emulator
command: sdkmanager "system-images;android-19;google_apis;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-19;google_apis;armeabi-v7a"
command: sdkmanager "system-images;android-16;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-16;default;armeabi-v7a"
- run:
name: Launch emulator
command: export LD_LIBRARY_PATH=${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/qt/lib && emulator64-arm -avd test -noaudio -no-boot-anim -no-window -accel on

4
.gitignore vendored
View File

@ -46,5 +46,5 @@ ehthumbs.db
Thumbs.db
.idea/codeStyles/
.idea/caches/
./app/key.p12
./app/upload-key.jks
app/key.p12
app/upload-key.jks

View File

@ -10,6 +10,6 @@
[Pobierz wersję beta](https://play.google.com/store/apps/details?id=io.github.wulkanowy&amp;utm_source=vcs)
[Pobierz wersję rozwojową](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/app-debug-bitrise-signed.apk)
[Pobierz wersję DEV](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/0)
Androidowy klient dziennika VULCAN UONET+.

View File

@ -1,4 +1,5 @@
apply plugin: 'java-library'
apply plugin: 'kotlin'
apply plugin: 'org.sonarqube'
apply plugin: 'jacoco'
apply plugin: 'com.jfrog.bintray'
@ -31,6 +32,9 @@ dependencies {
implementation "org.jsoup:jsoup:$jsoup"
implementation "org.apache.commons:commons-lang3:$apacheLang"
implementation "com.google.code.gson:gson:$gson"
implementation "org.slf4j:slf4j-api:$slf4jApi"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
testImplementation "junit:junit:$junit"
testImplementation "org.mockito:mockito-core:$mockito"

View File

@ -3,12 +3,16 @@ package io.github.wulkanowy.api;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import io.github.wulkanowy.api.generic.School;
import io.github.wulkanowy.api.login.Login;
public class Client {
@ -23,14 +27,21 @@ public class Client {
private String symbol;
private String schoolId;
private List<School> schools;
private Date lastSuccessRequest;
private Cookies cookies = new Cookies();
Client(String email, String password, String symbol) {
private static final Logger logger = LoggerFactory.getLogger(Client.class);
Client(String email, String password, String symbol, String schoolId) {
this.email = email;
this.password = password;
this.symbol = symbol;
this.schoolId = schoolId;
setFullEndpointInfo(email);
}
@ -58,10 +69,19 @@ public class Client {
return;
}
this.symbol = new Login(this).login(email, password, symbol);
logger.info("Not logged. Login...");
clearCookies();
new Login(this).login(email, password, symbol);
lastSuccessRequest = new Date();
logger.info("Login successful on {} at {}", getHost(), new Date());
}
private boolean isLoggedIn() {
logger.trace("Last success request: {}", lastSuccessRequest);
logger.trace("Cookies: {}", getCookies().size());
return getCookies().size() > 0 && lastSuccessRequest != null &&
5 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime());
@ -75,23 +95,38 @@ public class Client {
this.symbol = symbol;
}
public void addCookies(Map<String, String> items) {
cookies.addItems(items);
}
private Map<String, String> getCookies() {
return cookies.getItems();
}
String getHost() {
public void clearCookies() {
cookies = new Cookies();
}
public String getHost() {
return host;
}
public void setSchools(List<School> schools) {
this.schools = schools;
this.schoolId = schools.get(0).getId();
}
public List<School> getSchools() throws IOException, VulcanException {
login();
return schools;
}
public String getSchoolId() throws IOException, VulcanException {
return schoolId != null ? schoolId : getSchools().get(0).getId();
}
String getFilledUrl(String url) {
return url
.replace("{schema}", protocol)
.replace("{host}", host.replace(":", "%253A"))
.replace("{symbol}", symbol);
.replace("{host}", host)
.replace("{symbol}", symbol)
.replace("{ID}", schoolId != null ? schoolId : "");
}
public Document getPageByUrl(String url) throws IOException, VulcanException {
@ -111,7 +146,11 @@ public class Client {
this.cookies.addItems(cookies);
}
Connection.Response response = Jsoup.connect(getFilledUrl(url))
url = getFilledUrl(url);
logger.debug("GET {}", url);
Connection.Response response = Jsoup.connect(url)
.followRedirects(true)
.cookies(getCookies())
.execute();
@ -128,7 +167,11 @@ public class Client {
}
public synchronized Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException {
Connection connection = Jsoup.connect(getFilledUrl(url));
url = getFilledUrl(url);
logger.debug("POST {}", url);
Connection connection = Jsoup.connect(url);
for (String[] data : params) {
connection.data(data[0], data[1]);
@ -150,7 +193,11 @@ public class Client {
public String getJsonStringByUrl(String url) throws IOException, VulcanException {
login();
Connection.Response response = Jsoup.connect(getFilledUrl(url))
url = getFilledUrl(url);
logger.debug("GET {}", url);
Connection.Response response = Jsoup.connect(url)
.followRedirects(true)
.ignoreContentType(true)
.cookies(getCookies())
@ -164,7 +211,11 @@ public class Client {
public String postJsonStringByUrl(String url, String[][] params) throws IOException, VulcanException {
login();
Connection connection = Jsoup.connect(getFilledUrl(url));
url = getFilledUrl(url);
logger.debug("POST {}", url);
Connection connection = Jsoup.connect(url);
for (String[] data : params) {
connection.data(data[0], data[1]);
@ -196,7 +247,7 @@ public class Client {
}
if ("Błąd strony".equals(title)) {
throw new NotLoggedInErrorException(title + " " + doc.selectFirst("p, body") + ", status: " + code);
throw new NotLoggedInErrorException(title + " " + doc.body() + ", status: " + code);
}
return doc;

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,9 +6,12 @@ import org.jsoup.nodes.Element;
import java.io.IOException;
import java.util.List;
public interface SnP {
import io.github.wulkanowy.api.generic.Diary;
import io.github.wulkanowy.api.generic.ParamItem;
import io.github.wulkanowy.api.generic.Semester;
import io.github.wulkanowy.api.generic.Student;
String getSchoolID();
public interface SnP {
void setDiaryID(String id);

View File

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

View File

@ -1,13 +1,20 @@
package io.github.wulkanowy.api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import io.github.wulkanowy.api.attendance.AttendanceStatistics;
import io.github.wulkanowy.api.attendance.AttendanceTable;
import io.github.wulkanowy.api.exams.ExamsWeek;
import io.github.wulkanowy.api.generic.School;
import io.github.wulkanowy.api.grades.GradesList;
import io.github.wulkanowy.api.grades.SubjectsList;
import io.github.wulkanowy.api.messages.Messages;
import io.github.wulkanowy.api.mobile.RegisterDevice;
import io.github.wulkanowy.api.mobile.RegisteredDevices;
import io.github.wulkanowy.api.notes.AchievementsList;
import io.github.wulkanowy.api.notes.NotesList;
import io.github.wulkanowy.api.school.SchoolInfo;
@ -22,23 +29,24 @@ public class Vulcan {
private Client client;
private String schoolId;
private String studentId;
private String diaryId;
private static final Logger logger = LoggerFactory.getLogger(Vulcan.class);
public void setCredentials(String email, String password, String symbol, String schoolId, String studentId, String diaryId) {
this.schoolId = schoolId;
this.studentId = studentId;
this.diaryId = diaryId;
client = new Client(email, password, symbol);
client = new Client(email, password, symbol, schoolId);
logger.debug("Client created with symbol " + symbol);
}
public Client getClient() throws NotLoggedInErrorException {
if (null == client) {
throw new NotLoggedInErrorException("Use setCredentials() method first");
throw new NotLoggedInErrorException("Vulcan must be initialized by calling setCredentials() prior to fetch data");
}
return client;
@ -46,7 +54,10 @@ public class Vulcan {
public String getSymbol() throws NotLoggedInErrorException {
return getClient().getSymbol();
}
public List<School> getSchools() throws VulcanException, IOException {
return getClient().getSchools();
}
public SnP getStudentAndParent() throws VulcanException, IOException {
@ -54,7 +65,7 @@ public class Vulcan {
return this.snp;
}
this.snp = new StudentAndParent(getClient(), schoolId, studentId, diaryId)
this.snp = new StudentAndParent(getClient(), studentId, diaryId)
.setUp();
return this.snp;
@ -108,6 +119,14 @@ public class Vulcan {
return new FamilyInformation(getStudentAndParent());
}
public RegisteredDevices getRegisteredDevices() throws VulcanException, IOException {
return new RegisteredDevices(getStudentAndParent());
}
public RegisterDevice getRegisterDevice() throws VulcanException, IOException {
return new RegisterDevice(getStudentAndParent());
}
public Messages getMessages() throws VulcanException {
return new Messages(getClient());
}

View File

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

View File

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

View File

@ -37,7 +37,8 @@ public class Day {
return dayName;
}
public void setDayName(String dayName) {
public Day setDayName(String 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 {

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

View File

@ -0,0 +1,7 @@
package io.github.wulkanowy.api.generic
data class School(
val name: String,
val id: String,
val current: Boolean
)

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.api;
package io.github.wulkanowy.api.generic;
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 {

View File

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

View File

@ -5,6 +5,8 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
@ -14,25 +16,29 @@ import io.github.wulkanowy.api.VulcanException;
public class Login {
static final String LOGIN_PAGE_URL = "{schema}://cufs.{host}/{symbol}/Account/LogOn" +
"?ReturnUrl=%2F{symbol}%2FFS%2FLS%3Fwa%3Dwsignin1.0%26wtrealm%3D" +
protected static final String LOGIN_PAGE_URL = "{schema}://cufs.{host}/{symbol}/Account/LogOn";
private static final String LOGIN_PAGE_URL_QUERY = "?ReturnUrl=%2F{symbol}%2FFS%2FLS%3Fwa%3Dwsignin1.0%26wtrealm%3D" +
"{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx%26wctx%3D" +
"{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx";
private Client client;
private static final Logger logger = LoggerFactory.getLogger(Login.class);
public Login(Client client) {
this.client = client;
}
public String login(String email, String password, String symbol) throws VulcanException, IOException {
public void login(String email, String password, String symbol) throws VulcanException, IOException {
Document certDoc = sendCredentials(email, password);
if ("Błąd".equals(certDoc.title())) {
throw new NotLoggedInErrorException(certDoc.selectFirst("body").text());
client.clearCookies();
throw new NotLoggedInErrorException(certDoc.body().text());
}
return sendCertificate(certDoc, symbol);
sendCertificate(certDoc, symbol);
}
Document sendCredentials(String email, String password) throws IOException, VulcanException {
@ -41,7 +47,7 @@ public class Login {
{"Password", password}
};
Document nextDoc = sendCredentialsData(credentials, LOGIN_PAGE_URL);
Document nextDoc = sendCredentialsData(credentials, LOGIN_PAGE_URL + LOGIN_PAGE_URL_QUERY.replace(":", "%253A"));
Element errorMessage = nextDoc.selectFirst(".ErrorMessage, #ErrorTextLabel");
if (null != errorMessage) {
@ -81,13 +87,14 @@ public class Login {
};
}
String sendCertificate(Document doc, String defaultSymbol) throws IOException, VulcanException {
void sendCertificate(Document doc, String defaultSymbol) throws IOException, VulcanException {
client.setSymbol(findSymbol(defaultSymbol, doc.select("input[name=wresult]").val()));
Document targetDoc = sendCertData(doc);
String title = targetDoc.title();
if ("Working...".equals(title)) { // on adfs login
logger.info("ADFS login");
title = sendCertData(targetDoc).title();
}
@ -96,10 +103,11 @@ public class Login {
}
if (!"Uonet+".equals(title)) {
logger.debug("Login failed. Body: {}", targetDoc.body());
throw new LoginErrorException("Expected page title `UONET+`, got " + title);
}
return client.getSymbol();
client.setSchools(new StartPage(client).getSchools(targetDoc));
}
private Document sendCertData(Document doc) throws IOException, VulcanException {

View File

@ -0,0 +1,46 @@
package io.github.wulkanowy.api.login
import io.github.wulkanowy.api.Client
import io.github.wulkanowy.api.VulcanException
import io.github.wulkanowy.api.generic.School
import org.jsoup.nodes.Document
import org.slf4j.LoggerFactory
import java.net.URL
class StartPage(val client: Client) {
private val logger = LoggerFactory.getLogger(StartPage::class.java)
fun getSchools(startPage: Document): MutableList<School> {
val schoolList = mutableListOf<School>()
val snpLinks = startPage.select(".panel.linkownia.pracownik.klient a[href*=\"uonetplus-opiekun\"]")
logger.debug("SnP links: {}", snpLinks.size)
if (snpLinks.isEmpty()) {
throw VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?")
}
snpLinks.map {
schoolList.add(School(
it.text(),
getExtractedSchoolSymbolFromUrl(it.attr("href")),
it == snpLinks.first()
))
}
return schoolList
}
internal fun getExtractedSchoolSymbolFromUrl(snpPageUrl: String): String {
val path = URL(snpPageUrl).path.split("/")
if (6 != path.size) {
logger.error("Expected snp url, got {}", snpPageUrl)
throw VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?")
}
return path[2]
}
}

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

View File

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

View File

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

View File

@ -3,36 +3,39 @@ package io.github.wulkanowy.api.timetable;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.api.generic.Lesson;
import io.github.wulkanowy.api.generic.Week;
import static io.github.wulkanowy.api.DateTimeUtilsKt.getDateAsTick;
import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate;
public class Timetable {
private static final String TIMETABLE_PAGE_URL = "Lekcja.mvc/PlanZajec?data=";
private SnP snp;
private static final Logger logger = LoggerFactory.getLogger(Timetable.class);
public Timetable(SnP snp) {
this.snp = snp;
}
public Week<TimetableDay> getWeekTable() throws IOException, ParseException, VulcanException {
public Week<TimetableDay> getWeekTable() throws IOException, VulcanException {
return getWeekTable("");
}
public Week<TimetableDay> getWeekTable(final String tick) throws IOException, ParseException, VulcanException {
Element table = snp.getSnPPageDocument(TIMETABLE_PAGE_URL + tick)
public Week<TimetableDay> getWeekTable(final String date) throws IOException, VulcanException {
Element table = snp.getSnPPageDocument(TIMETABLE_PAGE_URL + getDateAsTick(date))
.select(".mainContainer .presentData").first();
List<TimetableDay> days = getDays(table.select("thead th"));
@ -44,19 +47,20 @@ public class Timetable {
.setDays(days);
}
private List<TimetableDay> getDays(Elements tableHeaderCells) throws ParseException {
private List<TimetableDay> getDays(Elements tableHeaderCells) {
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>");
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT);
Date d = sdf.parse(dayHeaderCell[1].trim());
sdf.applyPattern("yyyy-MM-dd");
TimetableDay day = new TimetableDay();
day.setDayName(dayHeaderCell[0]);
day.setDate(sdf.format(d));
day.setDate(getFormattedDate(dayHeaderCell[1].trim()));
if (tableHeaderCells.get(i).hasClass("free-day")) {
day.setFreeDay(true);
@ -132,6 +136,11 @@ public class Timetable {
private void addLessonInfoFromElement(Lesson lesson, Element e) {
Elements spans = e.select("span");
if (spans.isEmpty()) {
logger.warn("Lesson span is empty");
return;
}
addTypeInfo(lesson, spans);
addNormalLessonInfo(lesson, spans);
addChangesInfo(lesson, spans);
@ -218,6 +227,10 @@ public class Timetable {
}
private String[] getLessonAndGroupInfoFromSpan(Element span) {
if (!span.text().contains("[")) {
return new String[] {span.text(), ""};
}
String[] subjectNameArray = span.text().split(" ");
String groupName = subjectNameArray[subjectNameArray.length - 1];

View File

@ -12,16 +12,24 @@ public class ClientTest {
}
@Test
public void setFullEndpointInfoTest() throws Exception {
Client client = new Client("http://fakelog.net\\\\admin", "pass", "Default");
public void setFullEndpointInfoTest() {
Client client = new Client("http://fakelog.cf\\\\admin", "pass", "Default", "123");
Assert.assertEquals("fakelog.net", client.getHost());
Assert.assertEquals("fakelog.cf", client.getHost());
Assert.assertEquals("Default", client.getSymbol());
}
@Test
public void setFullEndpointInfoWithSymbolTest() {
Client client = new Client("http://fakelog.cf/notdefault\\\\admin", "pass", "Default", "123");
Assert.assertEquals("fakelog.cf", client.getHost());
Assert.assertEquals("notdefault", client.getSymbol()); //
}
@Test
public void checkForNoErrorsTest() throws Exception {
Client client = new Client("", "", "");
Client client = new Client("", "", "", "123");
Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-success.html"));
@ -30,7 +38,7 @@ public class ClientTest {
@Test(expected = VulcanOfflineException.class)
public void checkForErrorsOffline() throws Exception {
Client client = new Client("", "", "");
Client client = new Client("", "", "", "123");
Document doc = Jsoup.parse(getFixtureAsString("login/PrzerwaTechniczna.html"));
@ -39,7 +47,7 @@ public class ClientTest {
@Test(expected = NotLoggedInErrorException.class)
public void checkForErrors() throws Exception {
Client client = new Client("", "", "");
Client client = new Client("", "", "", "123");
Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-notLoggedIn.html"));
@ -48,16 +56,23 @@ public class ClientTest {
@Test
public void getFilledUrlTest() throws Exception {
Client client = new Client("http://fakelog.cf\\\\admin", "", "symbol123");
Client client = new Client("http://fakelog.cf\\\\admin", "", "symbol123", "321");
Assert.assertEquals("http://uonetplus.fakelog.cf/symbol123/LoginEndpoint.aspx",
client.getFilledUrl("{schema}://uonetplus.{host}/{symbol}/LoginEndpoint.aspx"));
Assert.assertEquals("http://uonetplus-opiekun.fakelog.cf/symbol123/321/Oceny/Wszystkie",
client.getFilledUrl("{schema}://uonetplus-opiekun.{host}/{symbol}/{ID}/Oceny/Wszystkie"));
}
@Test
public void getSymbolTest() throws Exception {
Client client = new Client("", "", "symbol4321");
public void getSymbolTest() {
Client client = new Client("", "", "symbol4321", "123");
Assert.assertEquals("symbol4321", client.getSymbol());
}
@Test
public void getSchoolIdTest() throws Exception {
Client client = new Client("", "", "1", "123456");
Assert.assertEquals("123456", client.getSchoolId());
}
}

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.List;
import io.github.wulkanowy.api.generic.Semester;
public class StudentAndParentTest {
private Client client;
@ -30,68 +32,13 @@ public class StudentAndParentTest {
@Test
public void snpTest() {
StudentAndParent snp = new StudentAndParent(client, "id123", null, null);
Assert.assertEquals("id123", snp.getSchoolID());
}
@Test
public void getSnpPageUrlWithIdTest() throws Exception {
Assert.assertEquals("{schema}://uonetplus-opiekun.{host}/{symbol}/123456/",
(new StudentAndParent(client, "123456", null, null)).getSnpHomePageUrl());
}
@Test
public void getSnpPageUrlWithoutIdTest() throws Exception {
String input = FixtureHelper.getAsString(getClass().getResourceAsStream("Start.html"));
Document startPageDocument = Jsoup.parse(input);
Mockito.when(client.getHost()).thenReturn("vulcan.net.pl");
Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(startPageDocument);
StudentAndParent snp = new StudentAndParent(client, null, null, null);
Assert.assertEquals("https://uonetplus-opiekun.vulcan.net.pl/symbol/534213/Start/Index/",
snp.getSnpHomePageUrl());
}
@Test(expected = VulcanException.class)
public void getSnpPageUrlWithWrongPage() throws Exception {
Document wrongPageDocument = Jsoup.parse(
FixtureHelper.getAsString(getClass().getResourceAsStream("OcenyWszystkie-semester.html"))
);
Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(wrongPageDocument);
StudentAndParent snp = new StudentAndParent(client, null, null, null);
snp.getSnpHomePageUrl();
}
@Test
public void getExtractedIDStandardTest() throws Exception {
Mockito.when(client.getHost()).thenReturn("vulcan.net.pl");
StudentAndParent snp = new StudentAndParent(client, "symbol", null, null);
Assert.assertEquals("123456", snp.getExtractedIdFromUrl("https://uonetplus-opiekun"
+ ".vulcan.net.pl/powiat/123456/Start/Index/"));
}
@Test
public void getExtractedIDDemoTest() throws Exception {
Mockito.when(client.getHost()).thenReturn("vulcan.net.pl");
StudentAndParent snp = new StudentAndParent(client, "symbol", null, null);
Assert.assertEquals("demo12345",
snp.getExtractedIdFromUrl("https://uonetplus-opiekun.vulcan.net.pl/demoupowiat/demo12345/Start/Index/"));
}
@Test(expected = NotLoggedInErrorException.class)
public void getExtractedIDNotLoggedTest() throws Exception {
Mockito.when(client.getHost()).thenReturn("vulcan.net.pl");
StudentAndParent snp = new StudentAndParent(client, "symbol", null, null);
Assert.assertEquals("123",
snp.getExtractedIdFromUrl("https://uonetplus.vulcan.net.pl/powiat/"));
StudentAndParent snp = new StudentAndParent(client, "1234", null);
Assert.assertEquals("1234", snp.getStudentID());
}
@Test
public void getSemestersTest() throws Exception {
SnP snp = new StudentAndParent(client, "123456", null, null);
SnP snp = new StudentAndParent(client, null, null);
List<Semester> semesters = snp.getSemesters();
Assert.assertEquals(2, semesters.size());
@ -111,7 +58,7 @@ public class StudentAndParentTest {
semesters.add(new Semester().setName("1500100900").setId("1").setCurrent(false));
semesters.add(new Semester().setName("1500100901").setId("2").setCurrent(true));
SnP snp = new StudentAndParent(client, "", null, null);
SnP snp = new StudentAndParent(client, null, null);
Semester semester = snp.getCurrent(semesters);
Assert.assertTrue(semester.isCurrent());
@ -121,7 +68,7 @@ public class StudentAndParentTest {
@Test
public void getCurrentSemesterFromEmptyTest() {
SnP snp = new StudentAndParent(client, "", null, null);
SnP snp = new StudentAndParent(client, null, null);
List<Semester> semesters = new ArrayList<>();
Assert.assertNull(snp.getCurrent(semesters));
@ -129,12 +76,12 @@ public class StudentAndParentTest {
@Test
public void getDiariesAndStudentTest() throws IOException, VulcanException {
Document snpHome = Jsoup.parse(FixtureHelper.getAsString(
getClass().getResourceAsStream("StudentAndParent.html")));
String input = FixtureHelper.getAsString(getClass().getResourceAsStream("WitrynaUczniaIRodzica.html"));
Document snpHome = Jsoup.parse(input);
client = Mockito.mock(Client.class);
Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(snpHome);
SnP snp = new StudentAndParent(client, "", null, null);
SnP snp = new StudentAndParent(client, null, null);
snp.setUp();

View File

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

View File

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

View File

@ -38,9 +38,11 @@ public class LoginTest {
.thenReturn(getFixtureAsDocument("Logowanie-certyfikat.html"));
Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString());
Mockito.when(client.getSymbol()).thenCallRealMethod();
Mockito.when(client.getHost()).thenReturn("fakelog.cf");
Login login = new Login(client);
login.login("a@a", "pswd", "d123");
Assert.assertEquals("d123", login.login("a@a", "pswd", "d123"));
Assert.assertEquals("d123", client.getSymbol());
}
@Test(expected = BadCredentialsException.class)
@ -74,10 +76,12 @@ public class LoginTest {
Client client = getClient("Logowanie-success.html");
Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString());
Mockito.when(client.getSymbol()).thenCallRealMethod();
Mockito.when(client.getHost()).thenReturn("fakelog.cf");
Login login = new Login(client);
Assert.assertEquals("wulkanowyschool321", login.sendCertificate(
getFixtureAsDocument("Logowanie-certyfikat.html"), "wulkanowyschool321"));
login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "wulkanowyschool321");
Assert.assertEquals("wulkanowyschool321", client.getSymbol());
}
@Test
@ -85,10 +89,12 @@ public class LoginTest {
Client client = getClient("Logowanie-success.html");
Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString());
Mockito.when(client.getSymbol()).thenCallRealMethod();
Mockito.when(client.getHost()).thenReturn("fakelog.cf");
Login login = new Login(client);
Assert.assertEquals("demo12345",
login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "Default"));
login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "Default");
Assert.assertEquals("demo12345", client.getSymbol());
}
@Test(expected = AccountPermissionException.class)

View File

@ -0,0 +1,68 @@
package io.github.wulkanowy.api.login
import io.github.wulkanowy.api.Client
import io.github.wulkanowy.api.FixtureHelper
import io.github.wulkanowy.api.VulcanException
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito
import org.mockito.Mockito.mock
class StartPageTest {
private val client: Client = mock(Client::class.java)
@Before fun setUp() {
Mockito.`when`(client.host).thenReturn("fakelog.cf")
}
private fun getDoc(name: String): Document = Jsoup.parse(FixtureHelper.getAsString(javaClass.getResourceAsStream(name)))
@Test fun getSchoolTest() {
assertEquals("534213", StartPage(client).getSchools(getDoc("../Start-std.html"))[0].id)
}
@Test fun getMultiSchoolTest() {
val schools = StartPage(client).getSchools(getDoc("../Start-multi.html"))
assertEquals("123456", schools[0].id)
assertEquals("123457", schools[1].id)
}
@Test fun getSchoolNameTest() {
assertEquals("Uczeń", StartPage(client).getSchools(getDoc("../Start-std.html"))[0].name)
}
@Test fun getMultiSchoolNameTest() {
val schools = StartPage(client).getSchools(getDoc("../Start-multi.html"))
assertEquals("GIMBB", schools[0].name)
assertEquals("SPBB", schools[1].name)
}
@Test(expected = VulcanException::class)
fun getSnpPageUrlWithWrongPage() {
StartPage(client).getSchools(getDoc("../OcenyWszystkie-semester.html"))
}
@Test
fun getExtractedIDStandardTest() {
assertEquals("123456", StartPage(client)
.getExtractedSchoolSymbolFromUrl("https://uonetplus-opiekun.fakelog.cf/powiat/123456/Start/Index/"))
}
@Test
fun getExtractedIDDemoTest() {
assertEquals("demo12345", StartPage(client)
.getExtractedSchoolSymbolFromUrl("https://uonetplus-opiekun.fakelog.cf/demoupowiat/demo12345/Start/Index/"))
}
@Test(expected = VulcanException::class)
fun getExtractedIDNotLoggedTest() {
assertEquals("123", StartPage(client)
.getExtractedSchoolSymbolFromUrl("https://uonetplus.NOTfakelog.cf/powiat/"))
}
}

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 {
List<Note> filledList = filled.getAllNotes();
Assert.assertEquals("06.06.2017", filledList.get(0).getDate());
Assert.assertEquals("01.10.2016", filledList.get(2).getDate());
Assert.assertEquals("2017-06-06", filledList.get(0).getDate());
Assert.assertEquals("2016-10-01", filledList.get(2).getDate());
}
@Test

View File

@ -156,6 +156,7 @@ public class TimetableTest extends StudentAndParentTestCase {
Assert.assertEquals("poprzednio: Wychowanie fizyczne", full.getWeekTable().getDay(4).getLesson(2).getDescription());
Assert.assertEquals("egzamin", full.getWeekTable().getDay(3).getLesson(0).getDescription());
Assert.assertEquals("", full.getWeekTable().getDay(4).getLesson(1).getDescription());
Assert.assertEquals("poprzednio: Zajęcia z wychowawcą", full.getWeekTable().getDay(4).getLesson(5).getDescription());
Assert.assertEquals("opis w uwadze bez klasy w spanie", full.getWeekTable().getDay(4).getLesson(4).getDescription());
Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getDescription());
}

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Uonet+</title>
</head>
<body>
<div class="startScreen">
<div class="holder">
<div class="content">
<div class="panel linkownia pracownik klient">
<div id="idAppUczen" class="subDiv">
<div class="appLink">
<a href="https://uonetplus-opiekun.fakelog.cf/symbol/123456/Start/Index/">
<span class="header directLink">GIMBB</span>
</a>
<br>
<a href="https://uonetplus-opiekun.fakelog.cf/symbol/123457/Start/Index/">
<span class="header directLink">SPBB</span>
</a>
<br>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Uonet+</title>
</head>
<body>
<div class="startScreen">
<div class="holder">
<div class="content">
<div class="panel linkownia pracownik klient">
<div class="appLink">
<a href="http://uonetplus-opiekun.fakelog.localhost:3000/default/534213/Start/Index/">
<div class="imagedHeader directLink">
<div id="idEmptyAppUczen">
<div class="name">Uczeń</div>
</div>
</div>
</a>
</div>
<div class="newAppLink">
<div class="link">
<a href="http://uonetplus-uczen.fakelog.localhost:3000/default/012345/">
<div class="imagedHeader directLink">
<div id="idEmptyAppUczenExt">
<div class="name">Uczeń</div>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,24 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Uonet+</title>
</head>
<body>
<div class="startScreen">
<div class="holder">
<div class="content">
<div class="panel linkownia pracownik klient">
<a href="https://uonetplus-opiekun.vulcan.net.pl/symbol/534213/Start/Index/">
<div class="imagedHeader directLink">
<div id="idEmptyAppUczeń">
<div class="name">Uczeń</div>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -11,6 +11,21 @@
<span class="userdata">example@wulkanowy.io (<a href="/demo123/LoginEndpoint.aspx?logout=true">wyloguj</a>)</span>
</div>
</div>
<div class="holder">
<div class="content">
<div class="panel linkownia pracownik klient">
<div class="appLink">
<a href="https://uonetplus-opiekun.fakelog.cf/symbol/534213/Start/Index/">
<div class="imagedHeader directLink">
<div id="idEmptyAppUczeń">
<div class="name">Uczeń</div>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

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

@ -364,7 +364,20 @@
<span></span>
</div>
</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>
<td>6</td>

View File

@ -5,7 +5,7 @@ sonarqube {
//noinspection GroovyAssignabilityCheck
properties {
def files = fileTree("${rootProject.projectDir}/api/build/libs/").filter { it.isFile() }.files.name
def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-26/android.jar," +
def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-27/android.jar," +
"${project.rootDir}/api/build/libs/" + files[0]
property "sonar.projectName", GROUP_ID + ":app"

View File

@ -1,3 +1,13 @@
apply plugin: 'org.greenrobot.greendao'
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' // sync warning probably caused by bug https://issuetracker.google.com/issues/74537216
apply plugin: 'io.fabric'
apply from: 'jacoco.gradle'
apply from: 'android-sonarqube.gradle'
apply plugin: 'com.google.gms.oss.licenses.plugin'
apply plugin: 'com.github.triplet.play'
buildscript {
repositories {
maven { url "https://plugins.gradle.org/m2/" }
@ -17,16 +27,8 @@ repositories {
maven { url 'https://maven.fabric.io/public' }
}
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'
apply plugin: 'io.fabric'
apply from: '../jacoco.gradle'
apply from: '../android-sonarqube.gradle'
apply plugin: 'com.google.gms.oss.licenses.plugin'
apply plugin: 'com.github.triplet.play'
android {
compileSdkVersion 26
compileSdkVersion 27
buildToolsVersion '27.0.3'
playAccountConfigs {
@ -40,9 +42,9 @@ android {
applicationId "io.github.wulkanowy"
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 15
targetSdkVersion 26
versionCode 13
versionName "0.4.5"
targetSdkVersion 27
versionCode 18
versionName "0.5.4"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
playAccountConfig = playAccountConfigs.defaultAccountConfig
@ -91,7 +93,7 @@ play {
}
greendao {
schemaVersion 28
schemaVersion 29
generateTests = true
}
@ -101,6 +103,7 @@ configurations.all {
dependencies {
implementation project(':api')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "com.android.support:support-v4:$supportVersion"
implementation "com.android.support:design:$supportVersion"
implementation "com.android.support:cardview-v7:$supportVersion"
@ -117,6 +120,8 @@ dependencies {
implementation "com.aurelhubert:ahbottomnavigation:$ahbottom"
implementation "com.jakewharton.threetenabp:threetenabp:$threeTenABP"
implementation "com.google.android.gms:play-services-oss-licenses:$ossLicenses"
implementation "com.jakewharton.timber:timber:$timber"
implementation "at.favre.lib:slf4j-timber:$slf4jTimber"
implementation("com.crashlytics.sdk.android:crashlytics:$crashlyticsSdk@aar") {
transitive = true
@ -125,12 +130,11 @@ dependencies {
transitive = true
}
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger2"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger2"
annotationProcessor "com.jakewharton:butterknife-compiler:$butterknife"
kapt "com.google.dagger:dagger-compiler:$dagger2"
kapt "com.google.dagger:dagger-android-processor:$dagger2"
kapt "com.jakewharton:butterknife-compiler:$butterknife"
debugImplementation "com.amitshekhar.android:debug-db:$debugDb"
debugImplementation "net.zetetic:android-database-sqlcipher:$sqlcipher"
testImplementation "junit:junit:$junit"
testImplementation "org.mockito:mockito-core:$mockito"

56
app/jacoco.gradle Normal file
View File

@ -0,0 +1,56 @@
apply plugin: "jacoco"
jacoco {
toolVersion "0.8.1"
reportsDir = file("$buildDir/reports")
}
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
}
// run ./gradlew clean createDebugCoverageReport jacocoTestReport
task jacocoTestReport(type: JacocoReport) {
group = "Reporting"
description = "Generate Jacoco coverage reports"
reports {
xml.enabled = true
html.enabled = true
}
def excludes = [
"**/R.class",
"**/R\$*.class",
"**/*\$ViewInjector*.*",
"**/BuildConfig.*",
"**/Manifest*.*",
"**/*Test*.*",
"android/**/*.*",
"**/*Fragment.*",
"**/*Activity.*"
]
// generated classes
classDirectories = fileTree(
// Java generated classes on Android project (debug build)
dir: "$buildDir/intermediates/classes/debug",
excludes: excludes
) + fileTree(
// Kotlin generated classes on Android project (debug build)
dir: "$buildDir/tmp/kotlin-classes/debug",
excludes: excludes
)
// sources
sourceDirectories = files([
android.sourceSets.main.java.srcDirs,
"src/main/kotlin"
])
executionData = fileTree(
dir: project.projectDir,
includes: ["**/*.exec" , "**/*.ec"]
)
}

View File

@ -0,0 +1,22 @@
package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.test.AbstractDaoTestLongPk;
import io.github.wulkanowy.data.db.dao.entities.School;
import io.github.wulkanowy.data.db.dao.entities.SchoolDao;
public class SchoolTest extends AbstractDaoTestLongPk<SchoolDao, School> {
public SchoolTest() {
super(SchoolDao.class);
}
@Override
protected School createEntity(Long key) {
School entity = new School();
entity.setId(key);
entity.setCurrent(false);
return entity;
}
}

View File

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

View File

@ -12,11 +12,12 @@ import javax.inject.Inject;
import dagger.android.AndroidInjector;
import dagger.android.support.DaggerApplication;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.utils.Log;
import io.fabric.sdk.android.Fabric;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.di.DaggerAppComponent;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.FabricUtils;
import io.github.wulkanowy.utils.LoggerUtils;
import timber.log.Timber;
public class WulkanowyApp extends DaggerApplication {
@ -39,15 +40,18 @@ public class WulkanowyApp extends DaggerApplication {
if (repository.getSharedRepo().isUserLoggedIn()) {
try {
repository.getSyncRepo().initLastUser();
FabricUtils.logLogin("Open app", true);
} 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() {
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() {
@ -60,6 +64,7 @@ public class WulkanowyApp extends DaggerApplication {
)
.debuggable(BuildConfig.DEBUG)
.build());
Timber.plant(new LoggerUtils.CrashlyticsTree());
}
@Override

View File

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

View File

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

View File

@ -17,6 +17,8 @@ public interface DbContract {
List<Grade> getNewGrades(int semesterName);
long getCurrentSchoolId();
long getCurrentStudentId();
long getCurrentSymbolId();
@ -30,4 +32,6 @@ public interface DbContract {
long getCurrentSemesterId();
int getCurrentSemesterName();
void recreateDatabase();
}

View File

@ -21,8 +21,9 @@ import io.github.wulkanowy.data.db.dao.migrations.Migration23;
import io.github.wulkanowy.data.db.dao.migrations.Migration26;
import io.github.wulkanowy.data.db.dao.migrations.Migration27;
import io.github.wulkanowy.data.db.dao.migrations.Migration28;
import io.github.wulkanowy.data.db.dao.migrations.Migration29;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.LogUtils;
import timber.log.Timber;
@Singleton
public class DbHelper extends DaoMaster.OpenHelper {
@ -41,7 +42,7 @@ public class DbHelper extends DaoMaster.OpenHelper {
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
LogUtils.info("Cleaning user data oldVersion=" + oldVersion + " newVersion=" + newVersion);
Timber.i("Cleaning user data oldVersion=%s newVersion=%s", oldVersion, newVersion);
Database database = new StandardDatabase(db);
recreateDatabase(database);
}
@ -54,11 +55,11 @@ public class DbHelper extends DaoMaster.OpenHelper {
for (Migration migration : migrations) {
if (oldVersion < migration.getVersion()) {
try {
LogUtils.info("Applying migration to db schema v" + migration.getVersion() + "...");
Timber.i("Applying migration to db schema v%s...", migration.getVersion());
migration.runMigration(db, sharedPref, vulcan);
LogUtils.info("Migration " + migration.getVersion() + " complete");
Timber.i("Migration %s complete", migration.getVersion());
} catch (Exception e) {
e.printStackTrace();
Timber.e(e, "Failed to apply migration");
recreateDatabase(db);
break;
}
@ -67,7 +68,7 @@ public class DbHelper extends DaoMaster.OpenHelper {
}
private void recreateDatabase(Database db) {
LogUtils.info("Database is recreating...");
Timber.i("Database is recreating...");
sharedPref.setCurrentUserId(0);
DaoMaster.dropAllTables(db, true);
onCreate(db);
@ -79,6 +80,7 @@ public class DbHelper extends DaoMaster.OpenHelper {
migrations.add(new Migration26());
migrations.add(new Migration27());
migrations.add(new Migration28());
migrations.add(new Migration29());
// Sorting just to be safe, in case other people add migrations in the wrong order.
Comparator<Migration> migrationComparator = new Comparator<Migration>() {

View File

@ -1,13 +1,17 @@
package io.github.wulkanowy.data.db.dao;
import org.greenrobot.greendao.database.Database;
import java.util.List;
import javax.inject.Inject;
import io.github.wulkanowy.data.db.dao.entities.DaoMaster;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.DiaryDao;
import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.GradeDao;
import io.github.wulkanowy.data.db.dao.entities.SchoolDao;
import io.github.wulkanowy.data.db.dao.entities.Semester;
import io.github.wulkanowy.data.db.dao.entities.SemesterDao;
import io.github.wulkanowy.data.db.dao.entities.StudentDao;
@ -69,10 +73,18 @@ public class DbRepository implements DbContract {
return getCurrentSymbol().getId();
}
@Override
public long getCurrentSchoolId() {
return daoSession.getSchoolDao().queryBuilder().where(
SchoolDao.Properties.SymbolId.eq(getCurrentSymbolId()),
SchoolDao.Properties.Current.eq(true)
).unique().getId();
}
@Override
public long getCurrentStudentId() {
return daoSession.getStudentDao().queryBuilder().where(
StudentDao.Properties.SymbolId.eq(getCurrentSymbolId()),
StudentDao.Properties.SchoolId.eq(getCurrentSchoolId()),
StudentDao.Properties.Current.eq(true)
).unique().getId();
}
@ -109,4 +121,12 @@ public class DbRepository implements DbContract {
SemesterDao.Properties.Current.eq(true)
).unique();
}
@Override
public void recreateDatabase() {
Database database = daoSession.getDatabase();
DaoMaster.dropAllTables(database, true);
DaoMaster.createAllTables(database, true);
}
}

View File

@ -0,0 +1,179 @@
package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.DaoException;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Property;
import org.greenrobot.greendao.annotation.ToMany;
import java.util.List;
@Entity(
nameInDb = "Schools",
active = true
)
public class School {
@Id(autoincrement = true)
private Long id;
@Property(nameInDb = "symbol_id")
private Long symbolId;
@Property(nameInDb = "current")
private boolean current;
@Property(nameInDb = "real_id")
private String realId;
@Property(nameInDb = "name")
private String name;
@ToMany(referencedJoinProperty = "schoolId")
private List<Student> studentList;
/**
* Used to resolve relations
*/
@Generated(hash = 2040040024)
private transient DaoSession daoSession;
/**
* Used for active entity operations.
*/
@Generated(hash = 1796006707)
private transient SchoolDao myDao;
@Generated(hash = 975562398)
public School(Long id, Long symbolId, boolean current, String realId,
String name) {
this.id = id;
this.symbolId = symbolId;
this.current = current;
this.realId = realId;
this.name = name;
}
@Generated(hash = 1579966795)
public School() {
}
public Long getId() {
return this.id;
}
public School setId(Long id) {
this.id = id;
return this;
}
public Long getSymbolId() {
return this.symbolId;
}
public School setSymbolId(Long symbolId) {
this.symbolId = symbolId;
return this;
}
public boolean getCurrent() {
return this.current;
}
public School setCurrent(boolean current) {
this.current = current;
return this;
}
public String getRealId() {
return this.realId;
}
public School setRealId(String realId) {
this.realId = realId;
return this;
}
public String getName() {
return this.name;
}
public School setName(String name) {
this.name = name;
return this;
}
/**
* To-many relationship, resolved on first access (and after reset).
* Changes to to-many relations are not persisted, make changes to the target entity.
*/
@Generated(hash = 180118651)
public List<Student> getStudentList() {
if (studentList == null) {
final DaoSession daoSession = this.daoSession;
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
StudentDao targetDao = daoSession.getStudentDao();
List<Student> studentListNew = targetDao._querySchool_StudentList(id);
synchronized (this) {
if (studentList == null) {
studentList = studentListNew;
}
}
}
return studentList;
}
/**
* Resets a to-many relationship, making the next get call to query for a fresh result.
*/
@Generated(hash = 1628625923)
public synchronized void resetStudentList() {
studentList = null;
}
/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 128553479)
public void delete() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.delete(this);
}
/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 1942392019)
public void refresh() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.refresh(this);
}
/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}.
* Entity must attached to an entity context.
*/
@Generated(hash = 713229351)
public void update() {
if (myDao == null) {
throw new DaoException("Entity is detached from DAO context");
}
myDao.update(this);
}
/** called by internal mechanisms, do not call yourself. */
@Generated(hash = 234091322)
public void __setDaoSession(DaoSession daoSession) {
this.daoSession = daoSession;
myDao = daoSession != null ? daoSession.getSchoolDao() : null;
}
}

View File

@ -18,8 +18,8 @@ public class Student {
@Id(autoincrement = true)
private Long id;
@Property(nameInDb = "symbol_id")
private Long symbolId;
@Property(nameInDb = "school_id")
private Long schoolId;
@Property(nameInDb = "current")
private boolean current;
@ -45,10 +45,10 @@ public class Student {
@Generated(hash = 1943931642)
private transient StudentDao myDao;
@Generated(hash = 1334215952)
public Student(Long id, Long symbolId, boolean current, String realId, String name) {
@Generated(hash = 470181623)
public Student(Long id, Long schoolId, boolean current, String realId, String name) {
this.id = id;
this.symbolId = symbolId;
this.schoolId = schoolId;
this.current = current;
this.realId = realId;
this.name = name;
@ -66,12 +66,12 @@ public class Student {
this.id = id;
}
public Long getSymbolId() {
return this.symbolId;
public Long getSchoolId() {
return this.schoolId;
}
public Student setSymbolId(Long symbolId) {
this.symbolId = symbolId;
public Student setSchoolId(Long schoolId) {
this.schoolId = schoolId;
return this;
}

View File

@ -24,9 +24,6 @@ public class Symbol {
@Property(nameInDb = "host")
private String host;
@Property(nameInDb = "school_id")
private String schoolId;
@Property(nameInDb = "symbol")
private String symbol;
@ -34,7 +31,7 @@ public class Symbol {
private String type;
@ToMany(referencedJoinProperty = "symbolId")
private List<Student> studentList;
private List<School> schoolList;
/**
* Used to resolve relations
@ -48,13 +45,11 @@ public class Symbol {
@Generated(hash = 684907977)
private transient SymbolDao myDao;
@Generated(hash = 242774339)
public Symbol(Long id, Long userId, String host, String schoolId, String symbol,
String type) {
@Generated(hash = 1034469460)
public Symbol(Long id, Long userId, String host, String symbol, String type) {
this.id = id;
this.userId = userId;
this.host = host;
this.schoolId = schoolId;
this.symbol = symbol;
this.type = type;
}
@ -89,15 +84,6 @@ public class Symbol {
return this;
}
public String getSchoolId() {
return this.schoolId;
}
public Symbol setSchoolId(String schoolId) {
this.schoolId = schoolId;
return this;
}
public String getSymbol() {
return this.symbol;
}
@ -120,30 +106,28 @@ public class Symbol {
* To-many relationship, resolved on first access (and after reset).
* Changes to to-many relations are not persisted, make changes to the target entity.
*/
@Generated(hash = 604366458)
public List<Student> getStudentList() {
if (studentList == null) {
@Generated(hash = 1733082867)
public List<School> getSchoolList() {
if (schoolList == null) {
final DaoSession daoSession = this.daoSession;
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
StudentDao targetDao = daoSession.getStudentDao();
List<Student> studentListNew = targetDao._querySymbol_StudentList(id);
SchoolDao targetDao = daoSession.getSchoolDao();
List<School> schoolListNew = targetDao._querySymbol_SchoolList(id);
synchronized (this) {
if (studentList == null) {
studentList = studentListNew;
if (schoolList == null) {
schoolList = schoolListNew;
}
}
}
return studentList;
return schoolList;
}
/**
* Resets a to-many relationship, making the next get call to query for a fresh result.
*/
@Generated(hash = 1628625923)
public synchronized void resetStudentList() {
studentList = null;
/** Resets a to-many relationship, making the next get call to query for a fresh result. */
@Generated(hash = 1757777300)
public synchronized void resetSchoolList() {
schoolList = null;
}
/**

View File

@ -9,8 +9,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.github.wulkanowy.api.Diary;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.generic.Diary;
import io.github.wulkanowy.data.db.dao.DbHelper;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.security.Scrambler;

View File

@ -15,7 +15,7 @@ public class Migration27 implements DbHelper.Migration {
}
@Override
public void runMigration(Database db, SharedPrefContract sharedPref, Vulcan vulcan) throws Exception {
public void runMigration(Database db, SharedPrefContract sharedPref, Vulcan vulcan) {
ExamDao.dropTable(db, true);
ExamDao.createTable(db, true);

View File

@ -0,0 +1,60 @@
package io.github.wulkanowy.data.db.dao.migrations
import android.database.Cursor
import org.greenrobot.greendao.database.Database
import io.github.wulkanowy.api.Vulcan
import io.github.wulkanowy.data.db.dao.DbHelper
import io.github.wulkanowy.data.db.shared.SharedPrefContract
class Migration29 : DbHelper.Migration {
override fun getVersion(): Int? {
return 29
}
override fun runMigration(db: Database, sharedPref: SharedPrefContract, vulcan: Vulcan) {
createSchoolsTable(db)
modifyStudents(db)
insertSchool(db, getRealSchoolId(db))
}
private fun createSchoolsTable(db: Database) {
db.execSQL("DROP TABLE IF EXISTS \"Schools\";")
db.execSQL("CREATE TABLE IF NOT EXISTS \"Schools\" (" + //
"\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id
"\"symbol_id\" INTEGER," + // 1: symbolId
"\"current\" INTEGER NOT NULL ," + // 2: current
"\"real_id\" TEXT," + // 3: realId
"\"name\" TEXT);") // 4: name
}
private fun modifyStudents(db: Database) {
db.execSQL("ALTER TABLE Students ADD COLUMN school_id INTEGER")
db.execSQL("UPDATE Students SET school_id = '1'")
}
private fun getRealSchoolId(db: Database): String {
var cursor: Cursor? = null
try {
cursor = db.rawQuery("SELECT school_id FROM Symbols WHERE _id=?", arrayOf("1"))
return if (cursor!!.count > 0) {
cursor.moveToFirst()
cursor.getString(cursor.getColumnIndex("school_id"))
} else ""
} finally {
cursor!!.close()
}
}
private fun insertSchool(db: Database, realId: String) {
db.execSQL("INSERT INTO Schools(symbol_id, current, real_id, name) VALUES(" +
"\"1\"," +
"\"1\"," +
"\"" + realId + "\"," +
"\"Uczeń\"" +
")")
}
}

View File

@ -3,8 +3,6 @@ package io.github.wulkanowy.data.db.resources;
import android.content.Context;
import android.content.res.Resources;
import com.crashlytics.android.Crashlytics;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
@ -16,8 +14,8 @@ import io.github.wulkanowy.R;
import io.github.wulkanowy.api.NotLoggedInErrorException;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.utils.AppConstant;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.security.CryptoException;
import timber.log.Timber;
@Singleton
public class ResourcesRepository implements ResourcesContract {
@ -41,8 +39,7 @@ public class ResourcesRepository implements ResourcesContract {
@Override
public String getErrorLoginMessage(Exception exception) {
LogUtils.error(AppConstant.APP_NAME + " encountered a error", exception);
Crashlytics.logException(exception);
Timber.e(exception,"%s encountered a error", AppConstant.APP_NAME);
if (exception instanceof CryptoException) {
return resources.getString(R.string.encrypt_failed_text);

View File

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

View File

@ -69,6 +69,11 @@ public class SharedPrefRepository implements SharedPrefContract {
return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_ATTENDANCE_PRESENT, false);
}
@Override
public int getCurrentTheme() {
return Integer.parseInt(settingsSharedPref.getString(SettingsFragment.SHARED_KEY_THEME, "1"));
}
@Override
public int getServicesInterval() {
return Integer.parseInt(settingsSharedPref.getString(SettingsFragment.SHARED_KEY_SERVICES_INTERVAL, "60"));
@ -88,4 +93,10 @@ public class SharedPrefRepository implements SharedPrefContract {
public boolean isMobileDisable() {
return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_SERVICES_MOBILE_DISABLED, false);
}
@Override
public void cleanSharedPref() {
appSharedPref.edit().clear().apply();
settingsSharedPref.edit().clear().apply();
}
}

View File

@ -17,6 +17,8 @@ import io.github.wulkanowy.data.db.dao.entities.DaoMaster;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Diary;
import io.github.wulkanowy.data.db.dao.entities.DiaryDao;
import io.github.wulkanowy.data.db.dao.entities.School;
import io.github.wulkanowy.data.db.dao.entities.SchoolDao;
import io.github.wulkanowy.data.db.dao.entities.Semester;
import io.github.wulkanowy.data.db.dao.entities.Student;
import io.github.wulkanowy.data.db.dao.entities.StudentDao;
@ -24,9 +26,9 @@ import io.github.wulkanowy.data.db.dao.entities.Symbol;
import io.github.wulkanowy.data.db.dao.entities.SymbolDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.security.CryptoException;
import io.github.wulkanowy.utils.security.Scrambler;
import timber.log.Timber;
@Singleton
public class AccountSync {
@ -57,12 +59,15 @@ public class AccountSync {
daoSession.getDatabase().beginTransaction();
Timber.i("Register start");
try {
Account account = insertAccount(email, password);
Symbol symbolEntity = insertSymbol(account);
insertStudents(symbolEntity);
insertDiaries(symbolEntity);
insertSemesters();
School schoolEntity = insertSchools(symbolEntity);
Student student = insertStudents(schoolEntity);
Diary diary = insertDiaries(student);
insertSemesters(diary);
sharedPref.setCurrentUserId(account.getId());
@ -70,10 +75,12 @@ public class AccountSync {
} finally {
daoSession.getDatabase().endTransaction();
}
Timber.i("Register end");
}
private Account insertAccount(String email, String password) throws CryptoException {
LogUtils.debug("Register account: " + email);
Timber.d("Register account");
Account account = new Account()
.setEmail(email)
.setPassword(Scrambler.encrypt(email, password, context));
@ -82,43 +89,64 @@ public class AccountSync {
}
private Symbol insertSymbol(Account account) throws VulcanException, IOException {
LogUtils.debug("Register symbol: " + vulcan.getSymbol());
vulcan.getSchools();
Timber.d("Register symbol (%s)", vulcan.getSymbol());
Symbol symbol = new Symbol()
.setUserId(account.getId())
.setSchoolId(vulcan.getStudentAndParent().getSchoolID())
.setSymbol(vulcan.getSymbol());
daoSession.getSymbolDao().insert(symbol);
return symbol;
}
private void insertStudents(Symbol symbol) throws VulcanException, IOException {
List<Student> studentList = DataObjectConverter.studentsToStudentEntities(
vulcan.getStudentAndParent().getStudents(),
private School insertSchools(Symbol symbol) throws VulcanException, IOException {
List<School> schoolList = DataObjectConverter.schoolsToSchoolsEntities(
vulcan.getSchools(),
symbol.getId()
);
LogUtils.debug("Register students: " + studentList.size());
daoSession.getStudentDao().insertInTx(studentList);
Timber.d("Register schools (%s)", schoolList.size());
daoSession.getSchoolDao().insertInTx(schoolList);
return daoSession.getSchoolDao().queryBuilder().where(
SchoolDao.Properties.SymbolId.eq(symbol.getId()),
SchoolDao.Properties.Current.eq(true)
).unique();
}
private void insertDiaries(Symbol symbolEntity) throws VulcanException, IOException {
private Student insertStudents(School school) throws VulcanException, IOException {
List<Student> studentList = DataObjectConverter.studentsToStudentEntities(
vulcan.getStudentAndParent().getStudents(),
school.getId()
);
Timber.d("Register students (%s)", studentList.size());
daoSession.getStudentDao().insertInTx(studentList);
return daoSession.getStudentDao().queryBuilder().where(
StudentDao.Properties.SchoolId.eq(school.getId()),
StudentDao.Properties.Current.eq(true)
).unique();
}
private Diary insertDiaries(Student student) throws VulcanException, IOException {
List<Diary> diaryList = DataObjectConverter.diariesToDiaryEntities(
vulcan.getStudentAndParent().getDiaries(),
daoSession.getStudentDao().queryBuilder().where(
StudentDao.Properties.SymbolId.eq(symbolEntity.getId()),
StudentDao.Properties.Current.eq(true)
).unique().getId());
LogUtils.debug("Register diaries: " + diaryList.size());
student.getId()
);
Timber.d("Register diaries (%s)", diaryList.size());
daoSession.getDiaryDao().insertInTx(diaryList);
return daoSession.getDiaryDao().queryBuilder().where(
DiaryDao.Properties.StudentId.eq(student.getId()),
DiaryDao.Properties.Current.eq(true)
).unique();
}
private void insertSemesters() throws VulcanException, IOException {
private void insertSemesters(Diary diary) throws VulcanException, IOException {
List<Semester> semesterList = DataObjectConverter.semestersToSemesterEntities(
vulcan.getStudentAndParent().getSemesters(),
daoSession.getDiaryDao().queryBuilder().where(
DiaryDao.Properties.Current.eq(true)
).unique().getId());
LogUtils.debug("Register semesters: " + semesterList.size());
diary.getId()
);
Timber.d("Register semesters (%s)", semesterList.size());
daoSession.getSemesterDao().insertInTx(semesterList);
}
@ -130,15 +158,18 @@ public class AccountSync {
throw new NotRegisteredUserException("Can't find user id in SharedPreferences");
}
LogUtils.debug("Initialization current user id=" + userId);
Timber.d("Init current user (%s)", userId);
Account account = daoSession.getAccountDao().load(userId);
Symbol symbol = daoSession.getSymbolDao().queryBuilder().where(
SymbolDao.Properties.UserId.eq(account.getId())).unique();
School school = daoSession.getSchoolDao().queryBuilder().where(
SchoolDao.Properties.SymbolId.eq(symbol.getId())).unique();
Student student = daoSession.getStudentDao().queryBuilder().where(
StudentDao.Properties.SymbolId.eq(symbol.getId()),
StudentDao.Properties.SchoolId.eq(school.getId()),
StudentDao.Properties.Current.eq(true)
).unique();
@ -151,7 +182,7 @@ public class AccountSync {
account.getEmail(),
Scrambler.decrypt(account.getEmail(), account.getPassword()),
symbol.getSymbol(),
symbol.getSchoolId(),
school.getRealId(),
student.getRealId(),
diary.getValue()
);

View File

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

View File

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

View File

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

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

View File

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

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.data.sync;
import org.apache.commons.collections4.CollectionUtils;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
@ -21,8 +20,7 @@ import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.TimeUtils;
import timber.log.Timber;
@Singleton
public class TimetableSync {
@ -39,10 +37,10 @@ public class TimetableSync {
this.vulcan = vulcan;
}
public void syncTimetable(long diaryId, String date) throws IOException, ParseException, VulcanException {
public void syncTimetable(long diaryId, String date) throws IOException, VulcanException {
this.diaryId = diaryId;
io.github.wulkanowy.api.generic.Week<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());
long weekId = updateWeekInDb(weekDb, weekApi);
@ -51,15 +49,11 @@ public class TimetableSync {
daoSession.getTimetableLessonDao().saveInTx(lessonList);
LogUtils.debug("Synchronization timetable lessons (amount = " + lessonList.size() + ")");
}
private String getNormalizedDate(String date) throws ParseException {
return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : "";
Timber.d("Timetable synchronization complete (%s)", lessonList.size());
}
private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.timetable.TimetableDay> getWeekFromApi(String date)
throws IOException, ParseException, VulcanException {
throws IOException, VulcanException {
return vulcan.getTimetable().getWeekTable(date);
}

View File

@ -4,7 +4,6 @@ import android.app.PendingIntent;
import android.content.Context;
import android.support.v4.app.NotificationCompat;
import com.crashlytics.android.Crashlytics;
import com.firebase.jobdispatcher.Constraint;
import com.firebase.jobdispatcher.FirebaseJobDispatcher;
import com.firebase.jobdispatcher.GooglePlayDriver;
@ -22,12 +21,16 @@ import javax.inject.Inject;
import dagger.android.AndroidInjection;
import io.github.wulkanowy.R;
import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.sync.NotRegisteredUserException;
import io.github.wulkanowy.services.notifies.GradeNotify;
import io.github.wulkanowy.ui.main.MainActivity;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.FabricUtils;
import timber.log.Timber;
import static io.github.wulkanowy.utils.TimeUtilsKt.isHolidays;
public class SyncJob extends SimpleJobService {
@ -65,6 +68,12 @@ public class SyncJob extends SimpleJobService {
@Override
public int onRunJob(JobParameters job) {
if (isHolidays()) {
stop(getApplicationContext());
return JobService.RESULT_FAIL_NORETRY;
}
try {
repository.getSyncRepo().initLastUser();
repository.getSyncRepo().syncAll();
@ -74,13 +83,24 @@ public class SyncJob extends SimpleJobService {
if (!gradeList.isEmpty() && repository.getSharedRepo().isNotifyEnable()) {
showNotification();
}
FabricUtils.logLogin("Background", true);
return JobService.RESULT_SUCCESS;
} catch (NotRegisteredUserException e) {
logError(e);
stop(getApplicationContext());
return JobService.RESULT_FAIL_NORETRY;
} catch (BadCredentialsException e) {
logError(e);
repository.cleanAllData();
stop(getApplicationContext());
return JobService.RESULT_FAIL_NORETRY;
} catch (Exception e) {
logError(e);
return JobService.RESULT_FAIL_RETRY;
}
}
@ -122,7 +142,7 @@ public class SyncJob extends SimpleJobService {
}
private void logError(Exception e) {
Crashlytics.logException(e);
LogUtils.error("During background synchronization an error occurred", e);
FabricUtils.logLogin("Background", false);
Timber.e(e, "During background synchronization an error occurred");
}
}

View File

@ -54,5 +54,6 @@ public abstract class BaseActivity extends DaggerAppCompatActivity implements Ba
if (unbinder != null) {
unbinder.unbind();
}
invalidateOptionsMenu();
}
}

View File

@ -14,6 +14,7 @@ import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import javax.inject.Inject;
@ -217,6 +218,12 @@ public class LoginActivity extends BaseActivity implements LoginContract.View {
return findViewById(R.id.login_activity_container);
}
@Override
public void onSyncFailed() {
Toast.makeText(getApplicationContext(), R.string.login_sync_error, Toast.LENGTH_LONG).show();
}
private void onLoginProgressUpdate(String step, String message) {
loginProgressText.setText(String.format("%1$s/2 - %2$s...", step, message));
}

View File

@ -33,6 +33,8 @@ public interface LoginContract {
void showActionBar(boolean show);
void onSyncFailed();
}
interface Presenter extends BaseContract.Presenter<View> {
@ -45,7 +47,7 @@ public interface LoginContract {
void onLoginProgress(int step);
void onEndAsync(boolean success, Exception exception);
void onEndAsync(int success, Exception exception);
void onCanceledAsync();
}

View File

@ -83,22 +83,30 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
}
@Override
public void onEndAsync(boolean success, Exception exception) {
if (success) {
FabricUtils.logRegister(true, getRepository().getDbRepo().getCurrentSymbol().getSymbol());
getView().openMainActivity();
return;
} else if (exception instanceof BadCredentialsException) {
getView().setErrorPassIncorrect();
getView().showSoftInput();
} else if (exception instanceof AccountPermissionException) {
getView().setErrorSymbolRequired();
getView().showSoftInput();
} else {
FabricUtils.logRegister(false, symbol);
getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception));
public void onEndAsync(int success, Exception exception) {
switch (success) {
case LoginTask.LOGIN_AND_SYNC_SUCCESS:
FabricUtils.logRegister(true, getRepository().getDbRepo().getCurrentSymbol().getSymbol(), "Success");
getView().openMainActivity();
return;
case LoginTask.SYNC_FAILED:
FabricUtils.logRegister(true, symbol, exception.getMessage());
getView().onSyncFailed();
getView().openMainActivity();
return;
case LoginTask.LOGIN_FAILED:
if (exception instanceof BadCredentialsException) {
getView().setErrorPassIncorrect();
getView().showSoftInput();
} else if (exception instanceof AccountPermissionException) {
getView().setErrorSymbolRequired();
getView().showSoftInput();
} else {
FabricUtils.logRegister(false, symbol, exception.getMessage());
getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception));
}
break;
}
getView().showActionBar(true);
getView().showLoginProgress(false);
}

View File

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

View File

@ -2,9 +2,9 @@ package io.github.wulkanowy.ui.main;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.AppBarLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.view.View;
@ -18,6 +18,7 @@ import javax.inject.Named;
import butterknife.BindView;
import io.github.wulkanowy.R;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.services.jobs.SyncJob;
import io.github.wulkanowy.ui.base.BaseActivity;
import io.github.wulkanowy.ui.base.BasePagerAdapter;
@ -26,6 +27,7 @@ import io.github.wulkanowy.ui.main.exams.ExamsFragment;
import io.github.wulkanowy.ui.main.grades.GradesFragment;
import io.github.wulkanowy.ui.main.settings.SettingsFragment;
import io.github.wulkanowy.ui.main.timetable.TimetableFragment;
import io.github.wulkanowy.utils.CommonUtils;
public class MainActivity extends BaseActivity implements MainContract.View,
AHBottomNavigation.OnTabSelectedListener, OnFragmentIsReadyListener {
@ -41,6 +43,9 @@ public class MainActivity extends BaseActivity implements MainContract.View,
@BindView(R.id.main_activity_progress_bar)
View progressBar;
@BindView(R.id.main_activity_appbar)
AppBarLayout appBar;
@Named("Main")
@Inject
BasePagerAdapter pagerAdapter;
@ -48,6 +53,9 @@ public class MainActivity extends BaseActivity implements MainContract.View,
@Inject
MainContract.Presenter presenter;
@Inject
RepositoryContract repository;
public static Intent getStartIntent(Context context) {
return new Intent(context, MainActivity.class);
}
@ -56,7 +64,7 @@ public class MainActivity extends BaseActivity implements MainContract.View,
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
setSupportActionBar((Toolbar) findViewById(R.id.main_activity_toolbar));
injectViews();
presenter.attachView(this, getIntent().getIntExtra(EXTRA_CARD_ID_KEY, -1));
@ -88,6 +96,8 @@ public class MainActivity extends BaseActivity implements MainContract.View,
@Override
public boolean onTabSelected(int position, boolean wasSelected) {
presenter.onTabSelected(position, wasSelected);
appBar.setExpanded(true, true);
invalidateOptionsMenu();
return true;
}
@ -119,8 +129,8 @@ public class MainActivity extends BaseActivity implements MainContract.View,
R.drawable.ic_menu_other_24dp));
bottomNavigation.setAccentColor(getResources().getColor(R.color.colorPrimary));
bottomNavigation.setInactiveColor(Color.BLACK);
bottomNavigation.setBackgroundColor(getResources().getColor(R.color.colorBackgroundBottomNav));
bottomNavigation.setInactiveColor(CommonUtils.getThemeAttrColor(this, android.R.attr.textColorTertiary));
bottomNavigation.setDefaultBackgroundColor(CommonUtils.getThemeAttrColor(this, R.attr.bottomNavBackground));
bottomNavigation.setTitleState(AHBottomNavigation.TitleState.ALWAYS_SHOW);
bottomNavigation.setOnTabSelectedListener(this);
bottomNavigation.setCurrentItem(tabPosition);

View File

@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.main;
import android.support.annotation.NonNull;
import javax.inject.Inject;
@ -8,6 +7,8 @@ import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.ui.base.BasePresenter;
import static io.github.wulkanowy.utils.TimeUtilsKt.isHolidays;
public class MainPresenter extends BasePresenter<MainContract.View>
implements MainContract.Presenter {
@ -35,7 +36,7 @@ public class MainPresenter extends BasePresenter<MainContract.View>
getView().initiationBottomNav(tabPosition);
getView().initiationViewPager(tabPosition);
if (getRepository().getSharedRepo().isServicesEnable()) {
if (getRepository().getSharedRepo().isServicesEnable() && !isHolidays()) {
getView().startSyncService(getRepository().getSharedRepo().getServicesInterval(),
getRepository().getSharedRepo().isMobileDisable());
}

View File

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

View File

@ -10,10 +10,12 @@ import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.utils.TimeUtils;
import io.github.wulkanowy.utils.async.AbstractTask;
import io.github.wulkanowy.utils.async.AsyncListeners;
import static io.github.wulkanowy.utils.TimeUtilsKt.getFirstDayOfCurrentWeek;
import static io.github.wulkanowy.utils.TimeUtilsKt.getMondaysFromCurrentSchoolYear;
public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
implements AttendanceContract.Presenter, AsyncListeners.OnFirstLoadingListener {
@ -42,11 +44,11 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
}
if (dates.isEmpty()) {
dates = TimeUtils.getMondaysFromCurrentSchoolYear();
dates = getMondaysFromCurrentSchoolYear();
}
if (positionToScroll == 0) {
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
positionToScroll = dates.indexOf(getFirstDayOfCurrentWeek());
}
if (!isFirstSight) {

View File

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

View File

@ -23,11 +23,11 @@ import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.ui.main.attendance.AttendanceDialogFragment;
class AttendanceSubItem
extends AbstractSectionableItem<AttendanceSubItem.SubItemViewHolder, AttendanceHeaderItem> {
extends AbstractSectionableItem<AttendanceSubItem.SubItemViewHolder, AttendanceHeader> {
private AttendanceLesson lesson;
AttendanceSubItem(AttendanceHeaderItem header, AttendanceLesson lesson) {
AttendanceSubItem(AttendanceHeader header, AttendanceLesson lesson) {
super(header);
this.lesson = lesson;
}

View File

@ -8,7 +8,7 @@ public interface AttendanceTabContract {
interface View extends BaseContract.View {
void updateAdapterList(List<AttendanceHeaderItem> headerItems);
void updateAdapterList(List<AttendanceHeader> headerItems);
void onRefreshSuccess();

View File

@ -40,7 +40,7 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
AttendanceTabContract.Presenter presenter;
@Inject
FlexibleAdapter<AttendanceHeaderItem> adapter;
FlexibleAdapter<AttendanceHeader> adapter;
private boolean isFragmentVisible = false;
@ -83,7 +83,7 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
}
@Override
public void updateAdapterList(List<AttendanceHeaderItem> headerItems) {
public void updateAdapterList(List<AttendanceHeader> headerItems) {
adapter.updateDataSet(headerItems);
}

View File

@ -15,7 +15,7 @@ public abstract class AttendanceTabModule {
@PerChildFragment
@Provides
static FlexibleAdapter<AttendanceHeaderItem> provideAdapter() {
static FlexibleAdapter<AttendanceHeader> provideAdapter() {
return new FlexibleAdapter<>(null);
}
}

View File

@ -24,7 +24,7 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
private AbstractTask loadingTask;
private List<AttendanceHeaderItem> headerItems = new ArrayList<>();
private List<AttendanceHeader> headerItems = new ArrayList<>();
private String date;
@ -115,7 +115,7 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
for (Day day : dayList) {
day.resetAttendanceLessons();
AttendanceHeaderItem headerItem = new AttendanceHeaderItem(day);
AttendanceHeader headerItem = new AttendanceHeader(day);
if (isEmptyWeek) {
isEmptyWeek = day.getAttendanceLessons().isEmpty();

View File

@ -2,7 +2,6 @@ package io.github.wulkanowy.ui.main.exams;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
@ -13,7 +12,6 @@ import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.github.wulkanowy.R;
import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.base.BasePagerAdapter;

View File

@ -10,10 +10,12 @@ import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.utils.TimeUtils;
import io.github.wulkanowy.utils.async.AbstractTask;
import io.github.wulkanowy.utils.async.AsyncListeners;
import static io.github.wulkanowy.utils.TimeUtilsKt.getFirstDayOfCurrentWeek;
import static io.github.wulkanowy.utils.TimeUtilsKt.getMondaysFromCurrentSchoolYear;
public class ExamsPresenter extends BasePresenter<ExamsContract.View>
implements ExamsContract.Presenter, AsyncListeners.OnFirstLoadingListener {
@ -42,11 +44,11 @@ public class ExamsPresenter extends BasePresenter<ExamsContract.View>
}
if (dates.isEmpty()) {
dates = TimeUtils.getMondaysFromCurrentSchoolYear();
dates = getMondaysFromCurrentSchoolYear();
}
if (positionToScroll == 0) {
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
positionToScroll = dates.indexOf(getFirstDayOfCurrentWeek());
}
if (!isFirstSight) {

View File

@ -18,11 +18,11 @@ import eu.davidea.viewholders.FlexibleViewHolder;
import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.Day;
public class ExamsHeaderItem extends AbstractHeaderItem<ExamsHeaderItem.HeaderVieHolder> {
public class ExamsHeader extends AbstractHeaderItem<ExamsHeader.HeaderVieHolder> {
private Day day;
ExamsHeaderItem(Day day) {
ExamsHeader(Day day) {
this.day = day;
}
@ -32,7 +32,7 @@ public class ExamsHeaderItem extends AbstractHeaderItem<ExamsHeaderItem.HeaderVi
if (o == null || getClass() != o.getClass()) return false;
ExamsHeaderItem that = (ExamsHeaderItem) o;
ExamsHeader that = (ExamsHeader) o;
return new EqualsBuilder()
.append(day, that.day)

View File

@ -21,11 +21,11 @@ import io.github.wulkanowy.data.db.dao.entities.Exam;
import io.github.wulkanowy.ui.main.exams.ExamsDialogFragment;
public class ExamsSubItem
extends AbstractSectionableItem<ExamsSubItem.SubItemViewHolder, ExamsHeaderItem> {
extends AbstractSectionableItem<ExamsSubItem.SubItemViewHolder, ExamsHeader> {
private Exam exam;
ExamsSubItem(ExamsHeaderItem header, Exam exam) {
ExamsSubItem(ExamsHeader header, Exam exam) {
super(header);
this.exam = exam;
}

View File

@ -116,7 +116,7 @@ public class ExamsTabPresenter extends BasePresenter<ExamsTabContract.View>
for (Day day : dayList) {
day.resetExams();
ExamsHeaderItem headerItem = new ExamsHeaderItem(day);
ExamsHeader headerItem = new ExamsHeader(day);
List<Exam> examList = day.getExams();

View File

@ -12,7 +12,9 @@ public interface GradesContract {
interface View extends BaseContract.View, SwipeRefreshLayout.OnRefreshListener {
void updateAdapterList(List<GradeHeaderItem> headerItems);
void updateAdapterList(List<GradesHeader> headerItems);
void updateSummaryAdapterList(List<GradesSummarySubItem> summarySubItems);
void showNoItem(boolean show);
@ -28,6 +30,8 @@ public interface GradesContract {
boolean isMenuVisible();
void setSummaryAverages(String calculatedValue, String predictedValue, String finalValue );
}
interface Presenter extends BaseContract.Presenter<View> {

View File

@ -71,7 +71,7 @@ public class GradesDialogFragment extends DialogFragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.grade_dialog, container, false);
View view = inflater.inflate(R.layout.grades_dialog, container, false);
ButterKnife.bind(this, view);

View File

@ -13,6 +13,7 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
@ -27,17 +28,38 @@ import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
public class GradesFragment extends BaseFragment implements GradesContract.View {
@BindView(R.id.grade_fragment_summary_container)
View summary;
@BindView(R.id.grade_fragment_details_container)
View details;
@BindView(R.id.grade_fragment_recycler)
RecyclerView recyclerView;
@BindView(R.id.grade_fragment_summary_recycler)
RecyclerView summaryRecyclerView;
@BindView(R.id.grade_fragment_no_item_container)
View noItemView;
@BindView(R.id.grade_fragment_swipe_refresh)
SwipeRefreshLayout refreshLayout;
@BindView(R.id.grade_fragment_summary_predicted_average)
TextView predictedAverage;
@BindView(R.id.grade_fragment_summary_calculated_average)
TextView calculatedAverage;
@BindView(R.id.grade_fragment_summary_final_average)
TextView finalAverage;
@Inject
FlexibleAdapter<GradeHeaderItem> adapter;
FlexibleAdapter<GradesHeader> adapter;
@Inject
FlexibleAdapter<GradesSummarySubItem> summaryAdapter;
@Inject
GradesContract.Presenter presenter;
@ -66,43 +88,59 @@ public class GradesFragment extends BaseFragment implements GradesContract.View
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.semester_switch, menu);
menu.clear();
inflater.inflate(R.menu.grades_action_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_filter) {
presenter.onSemesterSwitchActive();
CharSequence[] items = new CharSequence[]{
getResources().getString(R.string.semester_text, 1),
getResources().getString(R.string.semester_text, 2),
};
new AlertDialog.Builder(getContext())
.setTitle(R.string.switch_semester)
.setNegativeButton(R.string.cancel, null)
.setSingleChoiceItems(items, this.currentSemester, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
presenter.onSemesterChange(which);
dialog.cancel();
}
}).show();
return true;
} else {
return super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.action_semester_switch:
presenter.onSemesterSwitchActive();
CharSequence[] items = new CharSequence[]{
getResources().getString(R.string.semester_text, 1),
getResources().getString(R.string.semester_text, 2),
};
new AlertDialog.Builder(getContext())
.setTitle(R.string.switch_semester)
.setNegativeButton(R.string.cancel, null)
.setSingleChoiceItems(items, this.currentSemester, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
presenter.onSemesterChange(which);
dialog.cancel();
}
}).show();
return true;
case R.id.action_summary_switch:
boolean isDetailsVisible = details.getVisibility() == View.VISIBLE;
item.setTitle(isDetailsVisible ? R.string.action_title_details : R.string.action_title_summary);
details.setVisibility(isDetailsVisible ? View.INVISIBLE : View.VISIBLE);
summary.setVisibility(isDetailsVisible ? View.VISIBLE : View.INVISIBLE);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
noItemView.setVisibility(View.GONE);
summary.setVisibility(View.INVISIBLE);
details.setVisibility(View.VISIBLE);
adapter.setAutoCollapseOnExpand(true);
adapter.setAutoScrollOnExpand(true);
adapter.expandItemsAtStartUp();
summaryAdapter.setDisplayHeadersAtStartUp(true);
recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext()));
recyclerView.setAdapter(adapter);
summaryRecyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext()));
summaryRecyclerView.setAdapter(summaryAdapter);
summaryRecyclerView.setNestedScrollingEnabled(false);
refreshLayout.setColorSchemeResources(android.R.color.black);
refreshLayout.setOnRefreshListener(this);
@ -116,6 +154,13 @@ public class GradesFragment extends BaseFragment implements GradesContract.View
}
}
@Override
public void setSummaryAverages(String calculatedValue, String predictedValue, String finalValue) {
calculatedAverage.setText(calculatedValue);
predictedAverage.setText(predictedValue);
finalAverage.setText(finalValue);
}
@Override
public void setActivityTitle() {
setTitle(getString(R.string.grades_text));
@ -141,10 +186,15 @@ public class GradesFragment extends BaseFragment implements GradesContract.View
}
@Override
public void updateAdapterList(List<GradeHeaderItem> headerItems) {
public void updateAdapterList(List<GradesHeader> headerItems) {
adapter.updateDataSet(headerItems);
}
@Override
public void updateSummaryAdapterList(List<GradesSummarySubItem> summarySubItems) {
summaryAdapter.updateDataSet(summarySubItems);
}
@Override
public void onRefreshSuccessNoGrade() {
showMessage(R.string.snackbar_no_grades);

View File

@ -20,14 +20,14 @@ import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.utils.AnimationUtils;
import io.github.wulkanowy.utils.GradeUtils;
public class GradeHeaderItem
extends AbstractExpandableHeaderItem<GradeHeaderItem.HeaderViewHolder, GradesSubItem> {
public class GradesHeader
extends AbstractExpandableHeaderItem<GradesHeader.HeaderViewHolder, GradesSubItem> {
private Subject subject;
private final boolean isShowSummary;
GradeHeaderItem(Subject subject, boolean isShowSummary) {
GradesHeader(Subject subject, boolean isShowSummary) {
this.subject = subject;
this.isShowSummary = isShowSummary;
}
@ -38,7 +38,7 @@ public class GradeHeaderItem
if (o == null || getClass() != o.getClass()) return false;
GradeHeaderItem that = (GradeHeaderItem) o;
GradesHeader that = (GradesHeader) o;
return new EqualsBuilder()
.append(subject, that.subject)
@ -54,7 +54,7 @@ public class GradeHeaderItem
@Override
public int getLayoutRes() {
return R.layout.grade_header;
return R.layout.grades_header;
}
@Override
@ -113,34 +113,46 @@ public class GradeHeaderItem
averageText.setText(getGradesAverageString());
predictedText.setText(resources.getString(R.string.info_grades_predicted_rating,
item.getPredictedRating()));
GradeUtils.getShortGradeValue(item.getPredictedRating())));
finalText.setText(resources.getString(R.string.info_grades_final_rating,
item.getFinalRating()));
GradeUtils.getShortGradeValue(item.getFinalRating())));
resetViews();
toggleSummaryText();
toggleSubjectText();
toggleSummary();
alertImage.setVisibility(isSubItemsReadAndSaveAlertView(subItems)
? View.INVISIBLE : View.VISIBLE);
}
private String getGradesAverageString() {
float average = GradeUtils.calculateWeightedAverage(item.getGradeList());
if (average < 0) {
return resources.getString(R.string.info_no_average);
}
return resources.getString(R.string.info_average_grades, average);
}
@Override
public void onClick(View view) {
super.onClick(view);
toggleSubjectText();
toggleSummaryText();
toggleSummary();
}
private void toggleSummaryText() {
if (isSummaryToggleable()) {
if (isExpand()) {
AnimationUtils.slideDown(predictedText);
AnimationUtils.slideDown(finalText);
} else {
AnimationUtils.slideUp(predictedText);
AnimationUtils.slideUp(finalText);
}
private void resetViews() {
subjectName.setMaxLines(1);
setDefaultSummaryVisibility(predictedText, item.getPredictedRating());
setDefaultSummaryVisibility(finalText, item.getFinalRating());
}
private void setDefaultSummaryVisibility(View view, String value) {
if (!"-".equals(value) && isShowSummary) {
view.setVisibility(View.VISIBLE);
} else {
view.setVisibility(View.GONE);
}
}
@ -152,10 +164,25 @@ public class GradeHeaderItem
}
}
private void resetViews() {
subjectName.setMaxLines(1);
predictedText.setVisibility(View.GONE);
finalText.setVisibility(View.GONE);
private void toggleSummary() {
toggleSummaryView(predictedText, item.getPredictedRating(), isExpand());
toggleSummaryView(finalText, item.getFinalRating(), isExpand());
}
private boolean isExpand() {
return adapter.isExpanded(getFlexibleAdapterPosition());
}
private void toggleSummaryView(View view, String value, boolean expand) {
if ("-".equals(value) || isShowSummary) {
return;
}
if (expand) {
AnimationUtils.slideDown(view);
} else {
AnimationUtils.slideUp(view);
}
}
private boolean isSubItemsReadAndSaveAlertView(List<GradesSubItem> subItems) {
@ -168,37 +195,5 @@ public class GradeHeaderItem
return isRead;
}
private String getGradesAverageString() {
float average = GradeUtils.calculate(item.getGradeList());
if (average < 0) {
return resources.getString(R.string.info_no_average);
}
return resources.getString(R.string.info_average_grades, average);
}
private boolean isExpand() {
return adapter.isExpanded(getFlexibleAdapterPosition());
}
private boolean isSummaryToggleable() {
boolean isSummaryEmpty = true;
if (!"-".equals(item.getPredictedRating()) || !"-".equals(item.getFinalRating())) {
isSummaryEmpty = false;
}
if (isSummaryEmpty) {
return false;
} else if (isShowSummary) {
predictedText.setVisibility(View.VISIBLE);
finalText.setVisibility(View.VISIBLE);
return false;
}
return true;
}
}
}

View File

@ -12,7 +12,12 @@ public abstract class GradesModule {
abstract GradesContract.Presenter provideGradesPresenter(GradesPresenter gradesPresenter);
@Provides
static FlexibleAdapter<GradeHeaderItem> provideGradesAdapter() {
static FlexibleAdapter<GradesHeader> provideGradesAdapter() {
return new FlexibleAdapter<>(null);
}
@Provides
static FlexibleAdapter<GradesSummarySubItem> provideGradesSummaryAdapter() {
return new FlexibleAdapter<>(null);
}
}

View File

@ -9,6 +9,7 @@ import org.threeten.bp.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
@ -18,6 +19,7 @@ import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.utils.FabricUtils;
import io.github.wulkanowy.utils.GradeUtils;
import io.github.wulkanowy.utils.async.AbstractTask;
import io.github.wulkanowy.utils.async.AsyncListeners;
@ -31,12 +33,20 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
private OnFragmentIsReadyListener listener;
private List<GradeHeaderItem> headerItems = new ArrayList<>();
private List<GradesHeader> headerItems = new ArrayList<>();
private List<GradesSummarySubItem> summarySubItems = new ArrayList<>();
private boolean isFirstSight = false;
private int semesterName;
private float finalAverage;
private float predictedAverage;
private float calculatedAverage;
@Inject
GradesPresenter(RepositoryContract repository) {
super(repository);
@ -56,7 +66,6 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
if (!isFirstSight) {
isFirstSight = true;
reloadGrades();
}
}
@ -76,12 +85,6 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
.putCustomAttribute("Name", semesterName));
}
private void reloadGrades() {
loadingTask = new AbstractTask();
loadingTask.setOnFirstLoadingListener(this);
loadingTask.execute();
}
@Override
public void onFragmentVisible(boolean isVisible) {
if (isVisible) {
@ -140,13 +143,17 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
boolean isShowSummary = getRepository().getSharedRepo().isShowGradesSummary();
headerItems = new ArrayList<>();
summarySubItems = new ArrayList<>();
for (Subject subject : subjectList) {
subject.resetGradeList();
List<Grade> gradeList = subject.getGradeList();
GradesSummaryHeader summaryHeader = new GradesSummaryHeader(subject, GradeUtils.calculateWeightedAverage(gradeList));
summarySubItems.add(new GradesSummarySubItem(summaryHeader, subject));
if (!gradeList.isEmpty()) {
GradeHeaderItem headerItem = new GradeHeaderItem(subject, isShowSummary);
GradesHeader headerItem = new GradesHeader(subject, isShowSummary);
List<GradesSubItem> subItems = new ArrayList<>();
@ -159,6 +166,10 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
headerItems.add(headerItem);
}
}
finalAverage = GradeUtils.calculateSubjectsAverage(subjectList, false);
predictedAverage = GradeUtils.calculateSubjectsAverage(subjectList, true);
calculatedAverage = GradeUtils.calculateDetailedSubjectsAverage(subjectList);
}
@Override
@ -170,9 +181,35 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
public void onEndLoadingAsync(boolean result, Exception exception) {
getView().showNoItem(headerItems.isEmpty());
getView().updateAdapterList(headerItems);
setSummaryAverages();
getView().updateSummaryAdapterList(summarySubItems);
listener.onFragmentIsReady();
}
private void setSummaryAverages() {
getView().setSummaryAverages(
getFormattedAverage(calculatedAverage),
getFormattedAverage(predictedAverage),
getFormattedAverage(finalAverage)
);
}
private String getFormattedAverage(float average) {
if (-1.0f == average) {
return "-- --";
}
return String.format(Locale.FRANCE, "%.2f", average);
}
private void reloadGrades() {
loadingTask = new AbstractTask();
loadingTask.setOnFirstLoadingListener(this);
loadingTask.execute();
}
private void cancelAsyncTasks() {
if (refreshTask != null) {
refreshTask.cancel(true);

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