1
0

Compare commits

...

50 Commits
0.4.0 ... 0.5.2

Author SHA1 Message Date
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
d4b172e022 Version 0.4.5 2018-05-28 11:32:39 +02:00
b4c765b482 [API] Fix timetable endpoint (#125) 2018-05-28 11:29:43 +02:00
3f1fff6d96 Implementation of a new Dagger injection (#103) 2018-05-26 20:44:06 +02:00
b59008a90f Version 0.4.4 2018-05-25 20:33:45 +02:00
74c0dda999 Fix lesson description from warning (#124) 2018-05-25 20:14:16 +02:00
2288ceffb8 Stop refreshing while semester switching window open (#123) 2018-05-25 19:10:34 +02:00
ffe8511e3f Add some fabric answers (#120) 2018-05-25 16:35:12 +02:00
34205e4a8b Fix autofill on api lvl 26 (#122) 2018-05-25 11:49:53 +02:00
ef648c7f8b Clear db before register (#121) 2018-05-24 22:25:24 +02:00
3592946e6f Fix api login(#119) 2018-05-23 19:21:35 +02:00
859f8dc319 Avoid null if cell second div not contain span (#118) 2018-05-23 18:44:04 +02:00
9c97962e89 Version 0.4.3 2018-05-19 23:37:32 +02:00
a4445a8a97 Fix api login error (#115) 2018-05-19 23:02:54 +02:00
3f54d13b6b Add condition to exam query (#116) 2018-05-19 21:11:04 +02:00
5685e73e46 Fix text alignment <= lv l20 (#117) 2018-05-19 19:58:33 +02:00
e9b357e92d Remove the root check (#114) 2018-05-19 12:48:12 +02:00
62bc00cd68 Delete non-existing lessons on sync (#112) 2018-05-19 12:30:17 +02:00
54e6aee82e Stop SyncJob if user is not registered in app (#113) 2018-05-16 20:43:29 +02:00
3000c077c4 Version 0.4.2 2018-05-14 22:20:17 +02:00
7d5072b529 Fix exams sync (#108) 2018-05-14 22:13:08 +02:00
052d5e3911 Stopping job when user not registered in app (#111) 2018-05-14 21:39:09 +02:00
0014b74c6b Fix certificate parsing issues (#109) 2018-05-14 21:12:45 +02:00
08dd316aee Fix adfs login (#110) 2018-05-14 20:56:17 +02:00
95caa21f2a Version 0.4.1 2018-05-13 18:16:39 +02:00
cd6e14b13b [API] Fix first login (#107) 2018-05-13 17:33:31 +02:00
2cf262130e [API] Get rid of NULL in timetable lesson descriptions (#106) 2018-05-13 16:02:51 +02:00
23183c9d7a Call of SyncJob only after user register in app (#105) 2018-05-13 15:25:56 +02:00
be8ee4c835 Fix parsing grade description with symbol containing special chars (#104) 2018-05-11 21:55:38 +02:00
223 changed files with 4190 additions and 2317 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
@ -199,24 +193,40 @@ jobs:
workflows:
version: 2
build_check_tests:
build-test-deploy:
jobs:
- build
- build:
filters:
tags:
only: /.*/
- lint:
filters:
tags:
only: /.*/
requires:
- build
- app-test:
filters:
tags:
only: /.*/
requires:
- build
- api-test:
filters:
tags:
only: /.*/
requires:
- build
- instrumented:
requires:
- build
filters:
tags:
only: /.*/
requires:
- build
- sonarcube:
filters:
tags:
only: /.*/
requires:
- build
- lint
@ -228,4 +238,6 @@ workflows:
- instrumented
filters:
tags:
only: /.*/
only: /\d+\.\d+\.\d+/
branches:
ignore: /.*/

View File

@ -4,10 +4,12 @@
[![Bitrise](https://img.shields.io/bitrise/daeff1893f3c8128/master.svg?token=Hjm1ACamk86JDeVVJHOeqQ&style=flat-square)](https://www.bitrise.io/app/daeff1893f3c8128)
[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy)
[![BCH compliance](https://bettercodehub.com/edge/badge/wulkanowy/wulkanowy?branch=master)](https://bettercodehub.com/)
[![Scrutinizer](https://img.shields.io/scrutinizer/g/wulkanowy/wulkanowy.svg)](https://scrutinizer-ci.com/g/wulkanowy/wulkanowy/?branch=master)
[![Known Vulnerabilities](https://snyk.io/test/github/wulkanowy/wulkanowy/badge.svg?targetFile=app%2Fbuild.gradle&style=flat-square)](https://snyk.io/test/github/wulkanowy/wulkanowy?targetFile=app%2Fbuild.gradle)
[![Scrutinizer](https://img.shields.io/scrutinizer/g/wulkanowy/wulkanowy.svg?style=flat-square)](https://scrutinizer-ci.com/g/wulkanowy/wulkanowy/?branch=master)
[![Bintray](https://img.shields.io/bintray/v/wulkanowy/wulkanowy/api.svg?style=flat-square)](https://bintray.com/wulkanowy/wulkanowy/api)
[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr)
[Pobierz wersję rozwojową](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/app-debug-bitrise-signed.apk)
[Pobierz wersję beta](https://play.google.com/store/apps/details?id=io.github.wulkanowy&amp;utm_source=vcs)
[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 Date lastSuccessRequest = null;
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,13 +69,21 @@ public class Client {
return;
}
this.cookies = new Cookies();
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 &&
29 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime());
5 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime());
}
@ -76,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 {
@ -103,7 +137,7 @@ public class Client {
return getPageByUrl(url, loginBefore, null);
}
public Document getPageByUrl(String url, boolean loginBefore, Map<String, String> cookies) throws IOException, VulcanException {
public synchronized Document getPageByUrl(String url, boolean loginBefore, Map<String, String> cookies) throws IOException, VulcanException {
if (loginBefore) {
login();
}
@ -112,14 +146,18 @@ public class Client {
this.cookies.addItems(cookies);
}
Connection.Response response = Jsoup.connect(getFilledUrl(url))
url = getFilledUrl(url);
logger.debug("GET {}", url);
Connection.Response response = Jsoup.connect(url)
.followRedirects(true)
.cookies(getCookies())
.execute();
this.cookies.addItems(response.cookies());
Document doc = checkForErrors(response.parse());
Document doc = checkForErrors(response.parse(), response.statusCode());
if (loginBefore) {
lastSuccessRequest = new Date();
@ -128,8 +166,12 @@ public class Client {
return doc;
}
public Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException {
Connection connection = Jsoup.connect(getFilledUrl(url));
public synchronized Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException {
url = getFilledUrl(url);
logger.debug("POST {}", url);
Connection connection = Jsoup.connect(url);
for (String[] data : params) {
connection.data(data[0], data[1]);
@ -143,13 +185,19 @@ public class Client {
this.cookies.addItems(response.cookies());
return checkForErrors(response.parse());
response.bufferUp(); // fixes cert parsing issues #109
return checkForErrors(response.parse(), response.statusCode());
}
public String getJsonStringByUrl(String url) throws IOException, VulcanException {
login();
Connection.Response response = Jsoup.connect(getFilledUrl(url))
url = getFilledUrl(url);
logger.debug("GET {}", url);
Connection.Response response = Jsoup.connect(url)
.followRedirects(true)
.ignoreContentType(true)
.cookies(getCookies())
@ -163,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]);
@ -181,7 +233,7 @@ public class Client {
return response.body();
}
Document checkForErrors(Document doc) throws VulcanException {
Document checkForErrors(Document doc, int code) throws VulcanException {
lastSuccessRequest = null;
String title = doc.select("title").text();
@ -195,7 +247,7 @@ public class Client {
}
if ("Błąd strony".equals(title)) {
throw new VulcanException("Nieznany błąd");
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.vulcan.net.pl
Document startPage = client.getPageByUrl(START_PAGE_URL);
Element studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a").first();
if (null == studentTileLink) {
throw new NotLoggedInErrorException("You are probably not logged in. Force login");
}
String snpPageUrl = studentTileLink.attr("href");
this.schoolID = getExtractedIdFromUrl(snpPageUrl);
return snpPageUrl;
}
String getExtractedIdFromUrl(String snpPageUrl) throws NotLoggedInErrorException {
String[] path = snpPageUrl.split(client.getHost())[1].split("/");
if (5 != path.length) {
throw new NotLoggedInErrorException("You are probably not logged in");
}
return path[2];
}
public String getRowDataChildValue(Element e, int index) {
return e.select(".daneWiersz .wartosc").get(index - 1).text();
}
@ -102,11 +73,15 @@ 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 ("Witryna ucznia i rodzica Strona główna".equals(doc.select("title").first().text())) {
if (!doc.title().startsWith("Witryna ucznia i rodzica")) {
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")) {
throw new VulcanException("Sesja została nieprawidłowo zainicjowana");
}
@ -114,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 {
@ -122,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");
@ -63,7 +57,7 @@ public class AttendanceTable {
for (int i = 1; i < size; i++) {
Lesson lesson = new Lesson();
lesson.setDate(days.get(i - 1).getDate());
lesson.setNumber(hours.get(0).text());
lesson.setNumber(Integer.valueOf(hours.get(0).text()));
addLessonDetails(lesson, hours.get(i));

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

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

View File

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

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(symbol, "").replaceFirst(", ", "");
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,29 +5,40 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import io.github.wulkanowy.api.Client;
import io.github.wulkanowy.api.NotLoggedInErrorException;
import io.github.wulkanowy.api.VulcanException;
public class Login {
private static final String LOGIN_PAGE_URL = "{schema}://cufs.{host}/{symbol}/Account/LogOn" +
"?ReturnUrl=%2F{symbol}%2FFS%2FLS%3Fwa%3Dwsignin1.0%26wtrealm%3D" +
protected static final String LOGIN_PAGE_URL = "{schema}://cufs.{host}/{symbol}/Account/LogOn";
private static final String LOGIN_PAGE_URL_QUERY = "?ReturnUrl=%2F{symbol}%2FFS%2FLS%3Fwa%3Dwsignin1.0%26wtrealm%3D" +
"{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx%26wctx%3D" +
"{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx";
private Client client;
private static final Logger logger = LoggerFactory.getLogger(Login.class);
public Login(Client client) {
this.client = client;
}
public String login(String email, String password, String symbol) throws VulcanException, IOException {
public void login(String email, String password, String symbol) throws VulcanException, IOException {
Document certDoc = sendCredentials(email, password);
return sendCertificate(certDoc, symbol);
if ("Błąd".equals(certDoc.title())) {
client.clearCookies();
throw new NotLoggedInErrorException(certDoc.body().text());
}
sendCertificate(certDoc, symbol);
}
Document sendCredentials(String email, String password) throws IOException, VulcanException {
@ -36,27 +47,29 @@ public class Login {
{"Password", password}
};
String nextUrl = LOGIN_PAGE_URL;
Document loginPage = client.getPageByUrl(nextUrl, false);
Document nextDoc = sendCredentialsData(credentials, LOGIN_PAGE_URL + LOGIN_PAGE_URL_QUERY.replace(":", "%253A"));
Element formFirst = loginPage.select("#form1").first();
if (null != formFirst) { // on adfs login
Document formSecond = client.postPageByUrl(
formFirst.attr("abs:action"),
getFormStateParams(formFirst, "", "")
);
credentials = getFormStateParams(formSecond, email, password);
nextUrl = formSecond.select("#form1").first().attr("abs:action");
}
Document html = client.postPageByUrl(nextUrl, credentials);
Element errorMessage = html.select(".ErrorMessage, #ErrorTextLabel").first();
Element errorMessage = nextDoc.selectFirst(".ErrorMessage, #ErrorTextLabel");
if (null != errorMessage) {
throw new BadCredentialsException(errorMessage.text());
}
return html;
return nextDoc;
}
private Document sendCredentialsData(String[][] credentials, String nextUrl) throws IOException, VulcanException {
Element formFirst = client.getPageByUrl(nextUrl, false).selectFirst("#form1");
if (null != formFirst) { // only on adfs login
Document formSecond = client.postPageByUrl(
formFirst.attr("abs:action"),
getFormStateParams(formFirst, "", "")
);
credentials = getFormStateParams(formSecond, credentials[0][1], credentials[1][1]);
nextUrl = formSecond.selectFirst("#form1").attr("abs:action");
}
return client.postPageByUrl(nextUrl, credentials);
}
private String[][] getFormStateParams(Element form, String email, String password) {
@ -74,17 +87,15 @@ public class Login {
};
}
String sendCertificate(Document doc, String defaultSymbol) throws IOException, VulcanException {
String certificate = doc.select("input[name=wresult]").val();
String symbol = findSymbol(defaultSymbol, certificate);
client.setSymbol(symbol);
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.select("title").text();
String title = targetDoc.title();
if ("Working...".equals(title)) { // on adfs login
title = sendCertData(targetDoc).select("title").text();
logger.info("ADFS login");
title = sendCertData(targetDoc).title();
}
if ("Logowanie".equals(title)) {
@ -92,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 symbol;
client.setSchools(new StartPage(client).getSchools(targetDoc));
}
private Document sendCertData(Document doc) throws IOException, VulcanException {
@ -108,7 +120,7 @@ public class Login {
});
}
private String findSymbol(String symbol, String certificate) {
private String findSymbol(String symbol, String certificate) throws AccountPermissionException {
if ("Default".equals(symbol)) {
return findSymbolInCertificate(certificate);
}
@ -116,15 +128,19 @@ public class Login {
return symbol;
}
String findSymbolInCertificate(String certificate) {
String findSymbolInCertificate(String certificate) throws AccountPermissionException {
Elements instances = Jsoup
.parse(certificate.replaceAll(":", ""), "", Parser.xmlParser())
.select("[AttributeName=\"UserInstance\"] samlAttributeValue");
if (instances.isEmpty()) {
if (instances.isEmpty()) { // on adfs login
return "";
}
if (instances.size() < 2) { // 1st index is always `Default`
throw new AccountPermissionException("First login detected, specify symbol");
}
return instances.get(1).text();
}
}

View File

@ -0,0 +1,45 @@
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
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")
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(),
getExtractedIdFromUrl(it.attr("href")),
it == snpLinks.first()
))
}
return schoolList
}
internal fun getExtractedIdFromUrl(snpPageUrl: String): String {
val path = snpPageUrl.split(client.host).getOrNull(1)?.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/PlanLekcji?data=";
private static final String TIMETABLE_PAGE_URL = "Lekcja.mvc/PlanZajec?data=";
private SnP snp;
private static final Logger logger = LoggerFactory.getLogger(Timetable.class);
public Timetable(SnP snp) {
this.snp = snp;
}
public Week<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);
@ -81,7 +85,7 @@ public class Timetable {
lesson.setStartTime(startEndEnd[0]);
lesson.setEndTime(startEndEnd[1]);
lesson.setDate(days.get(i - 2).getDate());
lesson.setNumber(hours.get(0).text());
lesson.setNumber(Integer.valueOf(hours.get(0).text()));
addLessonDetails(lesson, hours.get(i).select("div"));
@ -94,15 +98,21 @@ public class Timetable {
moveWarningToLessonNode(e);
switch (e.size()) {
case 2:
Element span = e.last().select("span").first();
if (span.hasClass(LessonTypes.CLASS_MOVED_OR_CANCELED)) {
lesson.setNewMovedInOrChanged(true);
lesson.setDescription("poprzednio: " + getLessonAndGroupInfoFromSpan(span)[0]);
}
case 1:
addLessonInfoFromElement(lesson, e.first());
break;
case 2:
Element span = e.last().selectFirst("span");
if (null == span) {
addLessonInfoFromElement(lesson, e.first());
} else if (span.hasClass(LessonTypes.CLASS_MOVED_OR_CANCELED)) {
lesson.setNewMovedInOrChanged(true);
lesson.setDescription("poprzednio: " + getLessonAndGroupInfoFromSpan(span)[0]);
addLessonInfoFromElement(lesson, e.first());
} else {
addLessonInfoFromElement(lesson, e.last());
}
break;
case 3:
addLessonInfoFromElement(lesson, e.get(1));
break;
@ -116,7 +126,9 @@ public class Timetable {
Elements warn = e.select(".uwaga-panel");
if (!warn.isEmpty()) {
e.select(".x-treelabel-rlz").last().text("(" + warn.text() + ")");
e.select("span").last()
.addClass("x-treelabel-rlz")
.text(warn.text());
e.remove(1);
}
}
@ -124,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);
@ -167,7 +184,8 @@ public class Timetable {
lesson.setRoom(spans.get(5).text());
lesson.setMovedOrCanceled(false);
lesson.setNewMovedInOrChanged(true);
lesson.setDescription(StringUtils.substringBetween(spans.last().text(), "(", ")")
lesson.setDescription(StringUtils.defaultString(StringUtils.substringBetween(
spans.last().text(), "(", ")"), spans.last().text())
+ " (poprzednio: " + spans.get(0).text() + ")");
} else if (9 == spans.size()) {
String[] subjectAndGroupInfo = getLessonAndGroupInfoFromSpan(spans.get(4));
@ -178,13 +196,15 @@ public class Timetable {
lesson.setMovedOrCanceled(false);
lesson.setNewMovedInOrChanged(true);
lesson.setDivisionIntoGroups(true);
lesson.setDescription(StringUtils.substringBetween(spans.last().text(), "(", ")")
lesson.setDescription(StringUtils.defaultString(StringUtils.substringBetween(
spans.last().text(), "(", ")"), spans.last().text())
+ " (poprzednio: " + getLessonAndGroupInfoFromSpan(spans.get(0))[0] + ")");
} else if (4 <= spans.size()) {
lesson.setSubject(spans.get(0).text());
lesson.setTeacher(spans.get(1).text());
lesson.setRoom(spans.get(2).text());
lesson.setDescription(StringUtils.substringBetween(spans.last().text(), "(", ")"));
lesson.setDescription(StringUtils.defaultString(StringUtils.substringBetween(
spans.last().text(), "(", ")"), spans.last().text()));
}
}
@ -207,12 +227,17 @@ 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];
return new String[]{
span.text().replace(" " + groupName, ""),
StringUtils.substringBetween(groupName, "[", "]")
StringUtils.defaultString(StringUtils.substringBetween(
groupName, "[", "]"), groupName)
};
}
}

View File

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

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 = NotLoggedInErrorException.class)
public void getSnpPageUrlWithWrongPage() throws Exception {
Document wrongPageDocument = Jsoup.parse(
FixtureHelper.getAsString(getClass().getResourceAsStream("OcenyWszystkie-semester.html"))
);
Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(wrongPageDocument);
StudentAndParent snp = new StudentAndParent(client, null, null, null);
snp.getSnpHomePageUrl();
}
@Test
public void getExtractedIDStandardTest() throws Exception {
Mockito.when(client.getHost()).thenReturn("vulcan.net.pl");
StudentAndParent snp = new StudentAndParent(client, "symbol", null, null);
Assert.assertEquals("123456", snp.getExtractedIdFromUrl("https://uonetplus-opiekun"
+ ".vulcan.net.pl/powiat/123456/Start/Index/"));
}
@Test
public void getExtractedIDDemoTest() throws Exception {
Mockito.when(client.getHost()).thenReturn("vulcan.net.pl");
StudentAndParent snp = new StudentAndParent(client, "symbol", null, null);
Assert.assertEquals("demo12345",
snp.getExtractedIdFromUrl("https://uonetplus-opiekun.vulcan.net.pl/demoupowiat/demo12345/Start/Index/"));
}
@Test(expected = NotLoggedInErrorException.class)
public void getExtractedIDNotLoggedTest() throws Exception {
Mockito.when(client.getHost()).thenReturn("vulcan.net.pl");
StudentAndParent snp = new StudentAndParent(client, "symbol", null, null);
Assert.assertEquals("123",
snp.getExtractedIdFromUrl("https://uonetplus.vulcan.net.pl/powiat/"));
StudentAndParent snp = new StudentAndParent(client, "1234", null);
Assert.assertEquals("1234", snp.getStudentID());
}
@Test
public void getSemestersTest() throws Exception {
SnP snp = new StudentAndParent(client, "123456", null, null);
SnP snp = new StudentAndParent(client, null, null);
List<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(6, 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,27 +54,29 @@ 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());
Assert.assertEquals("STR", list.get(4).getSymbol());
Assert.assertEquals("K", list.get(5).getSymbol());
Assert.assertEquals("+Odp", list.get(6).getSymbol());
}
@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());
Assert.assertEquals("", list.get(4).getDescription());
Assert.assertEquals("Kordian", list.get(5).getDescription());
Assert.assertEquals("Kordian", list.get(6).getDescription());
}
@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());
@ -84,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());
@ -94,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

@ -34,9 +34,15 @@ public class LoginTest {
Client client = getClient("Logowanie-success.html");
Mockito.when(client.getPageByUrl(Mockito.anyString(), Mockito.anyBoolean()))
.thenReturn(getFixtureAsDocument("Logowanie-error.html"));
Mockito.when(client.postPageByUrl(Mockito.eq(Login.LOGIN_PAGE_URL), Mockito.any(String[][].class)))
.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)
@ -57,39 +63,54 @@ public class LoginTest {
Login login = new Login(client);
Assert.assertEquals(
getFixtureAsString("cert-stock.xml").replaceAll("\\s+",""),
login.sendCredentials("a@a", "passwd").select("input[name=wresult]").attr("value").replaceAll("\\s+","")
getFixtureAsString("cert-stock.xml").replaceAll("\\s+", ""),
login.sendCredentials("a@a", "passwd")
.select("input[name=wresult]")
.attr("value")
.replaceAll("\\s+", "")
);
}
@Test
public void sendCertificateNotDefaultSymbolSuccessTest() throws Exception {
Login login = new Login(getClient("Logowanie-success.html"));
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(new Document(""), "wulkanowyschool321"));
login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "wulkanowyschool321");
Assert.assertEquals("wulkanowyschool321", client.getSymbol());
}
@Test
public void sendCertificateDefaultSymbolSuccessTest() throws Exception {
Login login = new Login(getClient("Logowanie-success.html"));
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)
public void sendCertificateAccountPermissionTest() throws Exception {
Login login = new Login(getClient("Logowanie-brak-dostepu.html"));
Client client = getClient("Logowanie-brak-dostepu.html");
login.sendCertificate(getFixtureAsDocument("cert-stock.xml"), "demo123");
Login login = new Login(client);
login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "demo123");
}
@Test(expected = LoginErrorException.class)
public void sendCertificateLoginErrorTest() throws Exception {
Login login = new Login(getClient("Logowanie-certyfikat.html")); // change to other document
login.sendCertificate(getFixtureAsDocument("cert-stock.xml"), "demo123");
login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "demo123");
}
@Test
@ -101,10 +122,10 @@ public class LoginTest {
Assert.assertEquals("demo12345", login.findSymbolInCertificate(certificate));
}
@Test
public void findSymbolInInvalidCertificateTest() throws Exception {
@Test(expected = AccountPermissionException.class)
public void findSymbolInCertificateWithoutSecondInstanceTest() throws Exception {
Login login = new Login(getClient("Logowanie-certyfikat.html"));
Assert.assertEquals("", login.findSymbolInCertificate("<xml></xml>")); // change to real cert with empty symbols
login.findSymbolInCertificate(getFixtureAsString("cert-no-symbols.xml"));
}
}

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)
.getExtractedIdFromUrl("https://uonetplus-opiekun.fakelog.cf/powiat/123456/Start/Index/"))
}
@Test
fun getExtractedIDDemoTest() {
assertEquals("demo12345", StartPage(client)
.getExtractedIdFromUrl("https://uonetplus-opiekun.fakelog.cf/demoupowiat/demo12345/Start/Index/"))
}
@Test(expected = VulcanException::class)
fun getExtractedIDNotLoggedTest() {
assertEquals("123", StartPage(client)
.getExtractedIdFromUrl("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

@ -83,11 +83,11 @@ public class TimetableTest extends StudentAndParentTestCase {
@Test
public void getLessonNumberTest() throws Exception {
Assert.assertEquals("2", std.getWeekTable().getDay(0).getLesson(1).getNumber());
Assert.assertEquals("5", std.getWeekTable().getDay(2).getLesson(4).getNumber());
Assert.assertEquals("0", full.getWeekTable().getDay(0).getLesson(0).getNumber());
Assert.assertEquals("13", full.getWeekTable().getDay(4).getLesson(13).getNumber());
Assert.assertEquals("3", holidays.getWeekTable().getDay(3).getLesson(3).getNumber());
Assert.assertEquals(2, std.getWeekTable().getDay(0).getLesson(1).getNumber());
Assert.assertEquals(5, std.getWeekTable().getDay(2).getLesson(4).getNumber());
Assert.assertEquals(0, full.getWeekTable().getDay(0).getLesson(0).getNumber());
Assert.assertEquals(13, full.getWeekTable().getDay(4).getLesson(13).getNumber());
Assert.assertEquals(3, holidays.getWeekTable().getDay(3).getLesson(3).getNumber());
}
@Test
@ -105,6 +105,7 @@ public class TimetableTest extends StudentAndParentTestCase {
Assert.assertEquals("Zajęcia techniczne", std.getWeekTable().getDay(2).getLesson(4).getSubject());
Assert.assertEquals("Wychowanie fizyczne", std.getWeekTable().getDay(1).getLesson(1).getSubject());
Assert.assertEquals("Język angielski", full.getWeekTable().getDay(0).getLesson(1).getSubject());
Assert.assertEquals("Wychowanie fizyczne", full.getWeekTable().getDay(0).getLesson(9).getSubject());
Assert.assertEquals("Wychowanie do życia w rodzinie", full.getWeekTable().getDay(2).getLesson(0).getSubject());
Assert.assertEquals("Wychowanie fizyczne", full.getWeekTable().getDay(3).getLesson(1).getSubject());
Assert.assertEquals("Uroczyste zakończenie roku szkolnego", full.getWeekTable().getDay(4).getLesson(0).getSubject());
@ -151,9 +152,12 @@ public class TimetableTest extends StudentAndParentTestCase {
Assert.assertEquals("zastępstwo (poprzednio: Wychowanie fizyczne)", full.getWeekTable().getDay(3).getLesson(1).getDescription());
Assert.assertEquals("", full.getWeekTable().getDay(4).getLesson(0).getDescription());
Assert.assertEquals("", full.getWeekTable().getDay(4).getLesson(1).getDescription());
Assert.assertEquals("bez nawiasów (poprzednio: Religia)", full.getWeekTable().getDay(4).getLesson(3).getDescription());
Assert.assertEquals("poprzednio: Wychowanie fizyczne", full.getWeekTable().getDay(4).getLesson(2).getDescription());
Assert.assertEquals("egzamin", full.getWeekTable().getDay(3).getLesson(0).getDescription());
Assert.assertEquals("", 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,27 @@
<!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">
<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>
</body>
</html>

View File

@ -9,7 +9,7 @@
<div class="holder">
<div class="content">
<div class="panel linkownia pracownik klient">
<a href="https://uonetplus-opiekun.vulcan.net.pl/symbol/534213/Start/Index/">
<a href="https://uonetplus-opiekun.fakelog.cf/symbol/534213/Start/Index/">
<div class="imagedHeader directLink">
<div id="idEmptyAppUczeń">
<div class="name">Uczeń</div>

View File

@ -100,6 +100,14 @@
<td>06.02.2017</td>
<td>Amelia Stępień</td>
</tr>
<tr>
<td>Język polski</td>
<td class="break-word"><span class="ocenaCzastkowa" style="color:#6ECD07;">5</span></td>
<td class="break-word">+Odp, Kordian</td>
<td>5,00</td>
<td>11.05.2017</td>
<td>Amelia Stępień</td>
</tr>
</tbody>
</table>
</main>

View File

@ -6,6 +6,9 @@
<body>
<div id="MainDiv">
<form>
<div class="LogOnBoard">
<h1 id="h1Default">Logowanie</h1>
</div>
<div class="ErrorMessage center">
Zła nazwa użytkownika lub hasło
</div>

View File

@ -11,6 +11,19 @@
<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">
<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>
</body>
</html>

View File

@ -0,0 +1,13 @@
<trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<trust:RequestSecurityTokenResponse Context="https://uonetplus.fakelog.cf/Default/LoginEndpoint.aspx">
<trust:RequestedSecurityToken>
<saml:Assertion AssertionID="_12345678-1234-1234-1234-1234567890ab" IssueInstant="2017-10-18T22:00:29.006Z" Issuer="CUFSTokenService" MajorVersion="1" MinorVersion="1" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">
<saml:AttributeStatement>
<saml:Attribute AttributeName="UserInstance" AttributeNamespace="http://schemas.fakelog.cf/ws/identity/claims">
<saml:AttributeValue>Default</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</trust:RequestedSecurityToken>
</trust:RequestSecurityTokenResponse>
</trust:RequestSecurityTokenResponseCollection>

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

@ -3,6 +3,40 @@
<head>
<meta charset="utf-8">
<title>Witryna ucznia i rodzica Plan lekcji</title>
<style>
table, th, td {
border: 1px solid;
border-collapse: collapse;
}
table td > div:not(:last-child) {
border-bottom: 5px solid silver;
padding-bottom: 5px;
margin-bottom: 5px;
}
table td div span:nth-child(1) {
display: block;
}
table span {
font-size: smaller;
}
.x-treelabel-ppl {
background: #99f;
font-style: italic;
}
.x-treelabel-rlz {
background: #f3a;
}
.x-treelabel-inv {
background: #f00;
}
.x-treelabel-zas {
background: #0f0;
}
table span:not([class]),
table span[class=""] {
background: #999;
}
</style>
</head>
<body>
<main class="mainContainer">
@ -238,7 +272,17 @@
<span></span>
</div>
</td>
<td></td>
<td>
<div>
<span class="x-treelabel-inv">Religia</span>
<span class="x-treelabel-inv">Cyranka Krystian</span>
<span class="x-treelabel-inv">3</span>
<span class="x-treelabel-ppl x-treelabel-zas">Wychowanie do życia w rodzinie</span>
<span class="x-treelabel-ppl x-treelabel-zas">Nowak Jadwiga</span>
<span class="x-treelabel-ppl x-treelabel-zas">3</span>
<span class="x-treelabel-rlz">bez nawiasów</span>
</div>
</td>
</tr>
<tr>
<td>4</td>
@ -275,7 +319,16 @@
<span></span>
</div>
</td>
<td></td>
<td>
<div>
<span class="x-treelabel-ppl x-treelabel-inv">Język polski</span>
<span class="x-treelabel-ppl x-treelabel-inv"> </span>
<span class="x-treelabel-ppl x-treelabel-inv">16</span>
<span class="random-class">(oddział nieobecny)</span>
</div>
<button type="button" class="uwaga-btn">Uwaga</button>
<div class="uwaga-panel">opis w uwadze bez klasy w spanie</div>
</td>
</tr>
<tr>
<td>5</td>
@ -311,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>
@ -417,7 +483,18 @@
<tr>
<td>9</td>
<td>14:50 15:35</td>
<td></td>
<td>
<div>
<span class="x-treelabel-ppl x-treelabel-zas">Wychowanie fizyczne [zaw2]</span>
<span class="x-treelabel-ppl x-treelabel-zas"></span>
<span class="x-treelabel-ppl x-treelabel-zas"></span>
<span class="x-treelabel-ppl x-treelabel-zas">G3</span>
<span class="x-treelabel-rlz">(przeniesiona z lekcji 7, 01.12.2017)</span>
</div>
<div>
<!--<span>WTF</span>-->
</div>
</td>
<td>
<div>
<span class="x-treelabel-ppl x-treelabel-inv">Język niemiecki [J1]</span>

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/" }
@ -9,7 +19,7 @@ buildscript {
classpath "org.greenrobot:greendao-gradle-plugin:$greenDaoGradle"
classpath "io.fabric.tools:gradle:$fabricGradle"
classpath "com.google.gms:oss-licenses:0.9.2"
classpath 'com.github.triplet.gradle:play-publisher:1.2.0'
classpath "com.github.triplet.gradle:play-publisher:$playPublisher"
}
}
@ -17,16 +27,8 @@ repositories {
maven { url 'https://maven.fabric.io/public' }
}
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'
apply plugin: 'io.fabric'
apply from: '../jacoco.gradle'
apply from: '../android-sonarqube.gradle'
apply plugin: 'com.google.gms.oss.licenses.plugin'
apply plugin: 'com.github.triplet.play'
android {
compileSdkVersion 26
compileSdkVersion 27
buildToolsVersion '27.0.3'
playAccountConfigs {
@ -40,9 +42,9 @@ android {
applicationId "io.github.wulkanowy"
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 15
targetSdkVersion 26
versionCode 8
versionName "0.4.0"
targetSdkVersion 27
versionCode 16
versionName "0.5.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
playAccountConfig = playAccountConfigs.defaultAccountConfig
@ -91,7 +93,7 @@ play {
}
greendao {
schemaVersion 26
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

@ -1,8 +1,7 @@
package io.github.wulkanowy;
import android.app.Application;
import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.answers.Answers;
import com.crashlytics.android.core.CrashlyticsCore;
import com.jakewharton.threetenabp.AndroidThreeTen;
@ -10,18 +9,17 @@ import org.greenrobot.greendao.query.QueryBuilder;
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.component.ApplicationComponent;
import io.github.wulkanowy.di.component.DaggerApplicationComponent;
import io.github.wulkanowy.di.modules.ApplicationModule;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.di.DaggerAppComponent;
import io.github.wulkanowy.utils.FabricUtils;
import io.github.wulkanowy.utils.LoggerUtils;
import timber.log.Timber;
public class WulkanowyApp extends Application {
protected ApplicationComponent applicationComponent;
public class WulkanowyApp extends DaggerApplication {
@Inject
RepositoryContract repository;
@ -31,12 +29,6 @@ public class WulkanowyApp extends Application {
super.onCreate();
AndroidThreeTen.init(this);
applicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(this))
.build();
applicationComponent.inject(this);
if (BuildConfig.DEBUG) {
enableDebugLog();
}
@ -48,27 +40,35 @@ public class WulkanowyApp extends Application {
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() {
Fabric.with(new Fabric.Builder(this)
.kits(new Crashlytics.Builder()
.core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
.build())
.kits(
new Crashlytics.Builder()
.core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
.build(),
new Answers()
)
.debuggable(BuildConfig.DEBUG)
.build());
Timber.plant(new LoggerUtils.CrashlyticsTree());
}
public ApplicationComponent getApplicationComponent() {
return applicationComponent;
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder().create(this);
}
}

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

@ -4,6 +4,7 @@ import java.util.List;
import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.data.db.dao.entities.Symbol;
import io.github.wulkanowy.data.db.dao.entities.Week;
public interface DbContract {
@ -16,10 +17,14 @@ public interface DbContract {
List<Grade> getNewGrades(int semesterName);
long getCurrentSchoolId();
long getCurrentStudentId();
long getCurrentSymbolId();
Symbol getCurrentSymbol();
long getCurrentDiaryId();
long getSemesterId(int name);
@ -27,4 +32,6 @@ public interface DbContract {
long getCurrentSemesterId();
int getCurrentSemesterName();
void recreateDatabase();
}

View File

@ -12,16 +12,18 @@ import java.util.Comparator;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.data.db.dao.entities.DaoMaster;
import io.github.wulkanowy.data.db.dao.migrations.Migration23;
import io.github.wulkanowy.data.db.dao.migrations.Migration26;
import io.github.wulkanowy.data.db.dao.migrations.Migration27;
import io.github.wulkanowy.data.db.dao.migrations.Migration28;
import io.github.wulkanowy.data.db.dao.migrations.Migration29;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.di.annotations.DatabaseInfo;
import io.github.wulkanowy.utils.LogUtils;
import timber.log.Timber;
@Singleton
public class DbHelper extends DaoMaster.OpenHelper {
@ -31,7 +33,7 @@ public class DbHelper extends DaoMaster.OpenHelper {
private final Vulcan vulcan;
@Inject
DbHelper(@ApplicationContext Context context, @DatabaseInfo String dbName,
DbHelper(Context context, @Named("dbName") String dbName,
SharedPrefContract sharedPref, Vulcan vulcan) {
super(context, dbName);
this.sharedPref = sharedPref;
@ -40,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);
}
@ -53,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;
}
@ -66,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);
@ -76,6 +78,9 @@ public class DbHelper extends DaoMaster.OpenHelper {
List<Migration> migrations = new ArrayList<>();
migrations.add(new Migration23());
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,17 +1,22 @@
package io.github.wulkanowy.data.db.dao;
import org.greenrobot.greendao.database.Database;
import java.util.List;
import javax.inject.Inject;
import io.github.wulkanowy.data.db.dao.entities.DaoMaster;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.DiaryDao;
import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.GradeDao;
import io.github.wulkanowy.data.db.dao.entities.SchoolDao;
import io.github.wulkanowy.data.db.dao.entities.Semester;
import io.github.wulkanowy.data.db.dao.entities.SemesterDao;
import io.github.wulkanowy.data.db.dao.entities.StudentDao;
import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.data.db.dao.entities.Symbol;
import io.github.wulkanowy.data.db.dao.entities.SymbolDao;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao;
@ -57,16 +62,29 @@ public class DbRepository implements DbContract {
}
@Override
public long getCurrentSymbolId() {
public Symbol getCurrentSymbol() {
return daoSession.getSymbolDao().queryBuilder().where(
SymbolDao.Properties.UserId.eq(sharedPref.getCurrentUserId())
).unique();
}
@Override
public long getCurrentSymbolId() {
return getCurrentSymbol().getId();
}
@Override
public long getCurrentSchoolId() {
return daoSession.getSchoolDao().queryBuilder().where(
SchoolDao.Properties.SymbolId.eq(getCurrentSymbolId()),
SchoolDao.Properties.Current.eq(true)
).unique().getId();
}
@Override
public long getCurrentStudentId() {
return daoSession.getStudentDao().queryBuilder().where(
StudentDao.Properties.SymbolId.eq(getCurrentSymbolId()),
StudentDao.Properties.SchoolId.eq(getCurrentSchoolId()),
StudentDao.Properties.Current.eq(true)
).unique().getId();
}
@ -103,4 +121,12 @@ public class DbRepository implements DbContract {
SemesterDao.Properties.Current.eq(true)
).unique();
}
@Override
public void recreateDatabase() {
Database database = daoSession.getDatabase();
DaoMaster.dropAllTables(database, true);
DaoMaster.createAllTables(database, true);
}
}

View File

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

View File

@ -4,13 +4,15 @@ import org.greenrobot.greendao.DaoException;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Index;
import org.greenrobot.greendao.annotation.Property;
import java.io.Serializable;
@Entity(
nameInDb = "Exams",
active = true
active = true,
indexes = {@Index(value = "dayId,entryDate,subjectAndGroup,type,teacher", unique = true)}
)
public class Exam implements Serializable {

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

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

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

@ -0,0 +1,24 @@
package io.github.wulkanowy.data.db.dao.migrations;
import org.greenrobot.greendao.database.Database;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.data.db.dao.DbHelper;
import io.github.wulkanowy.data.db.dao.entities.ExamDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
public class Migration27 implements DbHelper.Migration {
@Override
public Integer getVersion() {
return 27;
}
@Override
public void runMigration(Database db, SharedPrefContract sharedPref, Vulcan vulcan) {
ExamDao.dropTable(db, true);
ExamDao.createTable(db, true);
db.execSQL("UPDATE Weeks SET exams_synced = 0");
}
}

View File

@ -0,0 +1,20 @@
package io.github.wulkanowy.data.db.dao.migrations;
import org.greenrobot.greendao.database.Database;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.data.db.dao.DbHelper;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
public class Migration28 implements DbHelper.Migration {
@Override
public Integer getVersion() {
return 28;
}
@Override
public void runMigration(final Database db, final SharedPrefContract sharedPref, final Vulcan vulcan) throws Exception {
throw new Exception("No migrations");
}
}

View File

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

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

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

View File

@ -2,6 +2,8 @@ package io.github.wulkanowy.data.sync;
import android.content.Context;
import org.greenrobot.greendao.database.Database;
import java.io.IOException;
import java.util.List;
@ -11,20 +13,22 @@ import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.DaoMaster;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Diary;
import io.github.wulkanowy.data.db.dao.entities.DiaryDao;
import io.github.wulkanowy.data.db.dao.entities.School;
import io.github.wulkanowy.data.db.dao.entities.SchoolDao;
import io.github.wulkanowy.data.db.dao.entities.Semester;
import io.github.wulkanowy.data.db.dao.entities.Student;
import io.github.wulkanowy.data.db.dao.entities.StudentDao;
import io.github.wulkanowy.data.db.dao.entities.Symbol;
import io.github.wulkanowy.data.db.dao.entities.SymbolDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.security.CryptoException;
import io.github.wulkanowy.utils.security.Scrambler;
import timber.log.Timber;
@Singleton
public class AccountSync {
@ -39,7 +43,7 @@ public class AccountSync {
@Inject
AccountSync(DaoSession daoSession, SharedPrefContract sharedPref,
Vulcan vulcan, @ApplicationContext Context context) {
Vulcan vulcan, Context context) {
this.daoSession = daoSession;
this.sharedPref = sharedPref;
this.vulcan = vulcan;
@ -49,16 +53,21 @@ public class AccountSync {
public void registerUser(String email, String password, String symbol)
throws VulcanException, IOException, CryptoException {
clearUserData();
vulcan.setCredentials(email, password, symbol, null, null, null);
daoSession.getDatabase().beginTransaction();
Timber.i("Register start");
try {
Account account = insertAccount(email, password);
Symbol symbolEntity = insertSymbol(account);
insertStudents(symbolEntity);
insertDiaries(symbolEntity);
insertSemesters();
School schoolEntity = insertSchools(symbolEntity);
Student student = insertStudents(schoolEntity);
Diary diary = insertDiaries(student);
insertSemesters(diary);
sharedPref.setCurrentUserId(account.getId());
@ -66,10 +75,12 @@ public class AccountSync {
} finally {
daoSession.getDatabase().endTransaction();
}
Timber.i("Register end");
}
private Account insertAccount(String email, String password) throws CryptoException {
LogUtils.debug("Register account: " + email);
Timber.d("Register account");
Account account = new Account()
.setEmail(email)
.setPassword(Scrambler.encrypt(email, password, context));
@ -78,63 +89,87 @@ 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);
}
public void initLastUser() throws IOException, CryptoException {
public void initLastUser() throws CryptoException {
long userId = sharedPref.getCurrentUserId();
if (userId == 0) {
throw new IOException("Can't find saved user");
throw new NotRegisteredUserException("Can't find user id in SharedPreferences");
}
LogUtils.debug("Initialization current user id=" + userId);
Timber.d("Init current user (%s)", userId);
Account account = daoSession.getAccountDao().load(userId);
Symbol symbol = daoSession.getSymbolDao().queryBuilder().where(
SymbolDao.Properties.UserId.eq(account.getId())).unique();
School school = daoSession.getSchoolDao().queryBuilder().where(
SchoolDao.Properties.SymbolId.eq(symbol.getId())).unique();
Student student = daoSession.getStudentDao().queryBuilder().where(
StudentDao.Properties.SymbolId.eq(symbol.getId()),
StudentDao.Properties.SchoolId.eq(school.getId()),
StudentDao.Properties.Current.eq(true)
).unique();
@ -147,9 +182,16 @@ public class AccountSync {
account.getEmail(),
Scrambler.decrypt(account.getEmail(), account.getPassword()),
symbol.getSymbol(),
symbol.getSchoolId(),
school.getRealId(),
student.getRealId(),
diary.getValue()
);
}
private void clearUserData() {
Database database = daoSession.getDatabase();
DaoMaster.dropAllTables(database, true);
DaoMaster.createAllTables(database, true);
sharedPref.setCurrentUserId(0);
}
}

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),
@ -135,6 +128,7 @@ public class ExamsSync {
.where(ExamDao.Properties.DayId.eq(dayId),
ExamDao.Properties.EntryDate.eq(examApi.getEntryDate()),
ExamDao.Properties.SubjectAndGroup.eq(examApi.getSubjectAndGroup()),
ExamDao.Properties.Type.eq(examApi.getType()),
ExamDao.Properties.Teacher.eq(examApi.getTeacher()))
.unique();
}

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

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

View File

@ -13,7 +13,7 @@ import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Semester;
import io.github.wulkanowy.data.db.dao.entities.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

@ -1,7 +1,8 @@
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;
@ -19,8 +20,7 @@ import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.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 {
@ -37,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);
@ -49,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);
}
@ -125,6 +121,16 @@ public class TimetableSync {
List<TimetableLesson> lessonsFromApiEntities = DataObjectConverter
.lessonsToTimetableLessonsEntities(lessons);
List<TimetableLesson> lessonsFromDbEntities = getLessonsFromDb(dayId);
if (!lessonsFromDbEntities.isEmpty()) {
List<TimetableLesson> lessonToRemove = new ArrayList<>(CollectionUtils.removeAll(lessonsFromDbEntities, lessonsFromApiEntities));
for (TimetableLesson timetableLesson : lessonToRemove) {
daoSession.getTimetableLessonDao().delete(timetableLesson);
}
}
for (TimetableLesson apiLessonEntity : lessonsFromApiEntities) {
TimetableLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId);
@ -148,4 +154,8 @@ public class TimetableSync {
TimetableLessonDao.Properties.EndTime.eq(apiEntity.getEndTime()))
.unique();
}
private List<TimetableLesson> getLessonsFromDb(long dayId) {
return daoSession.getDayDao().load(dayId).getTimetableLessons();
}
}

View File

@ -0,0 +1,20 @@
package io.github.wulkanowy.di;
import javax.inject.Singleton;
import dagger.Component;
import dagger.android.AndroidInjector;
import dagger.android.support.AndroidSupportInjectionModule;
import io.github.wulkanowy.WulkanowyApp;
@Singleton
@Component(modules = {
AndroidSupportInjectionModule.class,
AppModule.class,
BuilderModule.class
})
public interface AppComponent extends AndroidInjector<WulkanowyApp> {
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<WulkanowyApp> {
}
}

View File

@ -0,0 +1,78 @@
package io.github.wulkanowy.di;
import android.content.Context;
import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import io.github.wulkanowy.WulkanowyApp;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.data.Repository;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.data.db.dao.DbContract;
import io.github.wulkanowy.data.db.dao.DbHelper;
import io.github.wulkanowy.data.db.dao.DbRepository;
import io.github.wulkanowy.data.db.dao.entities.DaoMaster;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.resources.ResourcesContract;
import io.github.wulkanowy.data.db.resources.ResourcesRepository;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.data.db.shared.SharedPrefRepository;
import io.github.wulkanowy.data.sync.SyncContract;
import io.github.wulkanowy.data.sync.SyncRepository;
import io.github.wulkanowy.utils.AppConstant;
@Module
public abstract class AppModule {
@Binds
abstract Context provideContext(WulkanowyApp app);
@Singleton
@Binds
abstract RepositoryContract provideRepository(Repository repository);
@Singleton
@Binds
abstract DbContract provideDatabse(DbRepository dbRepository);
@Singleton
@Binds
abstract SharedPrefContract provideSharedPref(SharedPrefRepository sharedPrefRepository);
@Singleton
@Binds
abstract SyncContract provideSync(SyncRepository syncRepository);
@Singleton
@Binds
abstract ResourcesContract provideResources(ResourcesRepository resourcesRepository);
@Singleton
@Provides
static DaoSession provideDaoSession(DbHelper dbHelper) {
return new DaoMaster(dbHelper.getWritableDb()).newSession();
}
@Singleton
@Provides
static Vulcan provideVulcan() {
return new Vulcan();
}
@Provides
@Named("dbName")
static String provideDbName() {
return AppConstant.DATABASE_NAME;
}
@Provides
@Named("sharedPrefName")
static String provideSharedPrefName() {
return AppConstant.SHARED_PREFERENCES_NAME;
}
}

View File

@ -0,0 +1,39 @@
package io.github.wulkanowy.di;
import dagger.Module;
import dagger.android.ContributesAndroidInjector;
import io.github.wulkanowy.di.scopes.PerActivity;
import io.github.wulkanowy.services.jobs.SyncJob;
import io.github.wulkanowy.services.widgets.TimetableWidgetServices;
import io.github.wulkanowy.ui.login.LoginActivity;
import io.github.wulkanowy.ui.login.LoginModule;
import io.github.wulkanowy.ui.main.MainActivity;
import io.github.wulkanowy.ui.main.MainModule;
import io.github.wulkanowy.ui.splash.SplashActivity;
import io.github.wulkanowy.ui.splash.SplashModule;
import io.github.wulkanowy.ui.widgets.TimetableWidgetProvider;
@Module
abstract class BuilderModule {
@PerActivity
@ContributesAndroidInjector(modules = SplashModule.class)
abstract SplashActivity bindSplashActivity();
@PerActivity
@ContributesAndroidInjector(modules = LoginModule.class)
abstract LoginActivity bindLoginActivity();
@PerActivity
@ContributesAndroidInjector(modules = MainModule.class)
abstract MainActivity bindMainActivity();
@ContributesAndroidInjector
abstract SyncJob bindSyncJob();
@ContributesAndroidInjector
abstract TimetableWidgetServices bindTimetableWidgetServices();
@ContributesAndroidInjector
abstract TimetableWidgetProvider bindTimetableWidgetProvider();
}

View File

@ -1,11 +0,0 @@
package io.github.wulkanowy.di.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityContext {
}

View File

@ -1,11 +0,0 @@
package io.github.wulkanowy.di.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationContext {
}

View File

@ -1,11 +0,0 @@
package io.github.wulkanowy.di.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface SharedPreferencesInfo {
}

View File

@ -1,19 +0,0 @@
package io.github.wulkanowy.di.component;
import dagger.Component;
import io.github.wulkanowy.di.annotations.PerActivity;
import io.github.wulkanowy.di.modules.ActivityModule;
import io.github.wulkanowy.ui.login.LoginActivity;
import io.github.wulkanowy.ui.main.MainActivity;
import io.github.wulkanowy.ui.splash.SplashActivity;
@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
void inject(SplashActivity splashActivity);
void inject(LoginActivity loginActivity);
void inject(MainActivity mainActivity);
}

View File

@ -1,32 +0,0 @@
package io.github.wulkanowy.di.component;
import android.content.Context;
import javax.inject.Singleton;
import dagger.Component;
import io.github.wulkanowy.WulkanowyApp;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.di.modules.ApplicationModule;
import io.github.wulkanowy.services.jobs.SyncJob;
import io.github.wulkanowy.ui.widgets.TimetableWidgetFactory;
import io.github.wulkanowy.ui.widgets.TimetableWidgetProvider;
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
@ApplicationContext
Context getContext();
RepositoryContract getRepository();
void inject(WulkanowyApp wulkanowyApp);
void inject(SyncJob syncJob);
void inject(TimetableWidgetFactory timetableWidgetFactory);
void inject(TimetableWidgetProvider timetableWidgetProvider);
}

View File

@ -1,31 +0,0 @@
package io.github.wulkanowy.di.component;
import dagger.Component;
import io.github.wulkanowy.di.annotations.PerFragment;
import io.github.wulkanowy.di.modules.FragmentModule;
import io.github.wulkanowy.ui.main.attendance.AttendanceFragment;
import io.github.wulkanowy.ui.main.attendance.AttendanceTabFragment;
import io.github.wulkanowy.ui.main.exams.ExamsFragment;
import io.github.wulkanowy.ui.main.exams.ExamsTabFragment;
import io.github.wulkanowy.ui.main.grades.GradesFragment;
import io.github.wulkanowy.ui.main.timetable.TimetableFragment;
import io.github.wulkanowy.ui.main.timetable.TimetableTabFragment;
@PerFragment
@Component(dependencies = ApplicationComponent.class, modules = FragmentModule.class)
public interface FragmentComponent {
void inject(GradesFragment gradesFragment);
void inject(AttendanceFragment attendanceFragment);
void inject(AttendanceTabFragment attendanceTabFragment);
void inject(ExamsFragment examsFragment);
void inject(ExamsTabFragment examsTabFragment);
void inject(TimetableFragment timetableFragment);
void inject(TimetableTabFragment timetableTabFragment);
}

View File

@ -1,64 +0,0 @@
package io.github.wulkanowy.di.modules;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import dagger.Module;
import dagger.Provides;
import io.github.wulkanowy.di.annotations.ActivityContext;
import io.github.wulkanowy.di.annotations.PerActivity;
import io.github.wulkanowy.ui.base.BasePagerAdapter;
import io.github.wulkanowy.ui.login.LoginContract;
import io.github.wulkanowy.ui.login.LoginPresenter;
import io.github.wulkanowy.ui.main.MainContract;
import io.github.wulkanowy.ui.main.MainPresenter;
import io.github.wulkanowy.ui.splash.SplashContract;
import io.github.wulkanowy.ui.splash.SplashPresenter;
@Module
public class ActivityModule {
private AppCompatActivity activity;
public ActivityModule(AppCompatActivity activity) {
this.activity = activity;
}
@ActivityContext
@Provides
Context provideContext() {
return activity;
}
@Provides
AppCompatActivity provideActivity() {
return activity;
}
@PerActivity
@Provides
SplashContract.Presenter provideSplashPresenter
(SplashPresenter splashPresenter) {
return splashPresenter;
}
@PerActivity
@Provides
LoginContract.Presenter provideLoginPresenter
(LoginPresenter loginPresenter) {
return loginPresenter;
}
@PerActivity
@Provides
MainContract.Presenter provideMainPresenter
(MainPresenter mainPresenter) {
return mainPresenter;
}
@Provides
BasePagerAdapter provideMainPagerAdapter() {
return new BasePagerAdapter(activity.getSupportFragmentManager());
}
}

View File

@ -1,111 +0,0 @@
package io.github.wulkanowy.di.modules;
import android.app.Application;
import android.content.Context;
import com.firebase.jobdispatcher.FirebaseJobDispatcher;
import com.firebase.jobdispatcher.GooglePlayDriver;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.data.Repository;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.data.db.dao.DbContract;
import io.github.wulkanowy.data.db.dao.DbHelper;
import io.github.wulkanowy.data.db.dao.DbRepository;
import io.github.wulkanowy.data.db.dao.entities.DaoMaster;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.resources.ResourcesContract;
import io.github.wulkanowy.data.db.resources.ResourcesRepository;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.data.db.shared.SharedPrefRepository;
import io.github.wulkanowy.data.sync.SyncContract;
import io.github.wulkanowy.data.sync.SyncRepository;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.di.annotations.DatabaseInfo;
import io.github.wulkanowy.di.annotations.SharedPreferencesInfo;
import io.github.wulkanowy.utils.AppConstant;
@Module
public class ApplicationModule {
private final Application application;
public ApplicationModule(Application application) {
this.application = application;
}
@Provides
Application provideApplication() {
return application;
}
@ApplicationContext
@Provides
Context provideAppContext() {
return application;
}
@DatabaseInfo
@Provides
String provideDatabaseName() {
return AppConstant.DATABASE_NAME;
}
@SharedPreferencesInfo
@Provides
String provideSharedPreferencesName() {
return AppConstant.SHARED_PREFERENCES_NAME;
}
@Singleton
@Provides
DaoSession provideDaoSession(DbHelper dbHelper) {
return new DaoMaster(dbHelper.getWritableDb()).newSession();
}
@Singleton
@Provides
Vulcan provideVulcan() {
return new Vulcan();
}
@Singleton
@Provides
RepositoryContract provideRepository(Repository repository) {
return repository;
}
@Singleton
@Provides
SharedPrefContract provideSharedPref(SharedPrefRepository sharedPrefRepository) {
return sharedPrefRepository;
}
@Singleton
@Provides
ResourcesContract provideAppResources(ResourcesRepository resourcesRepository) {
return resourcesRepository;
}
@Singleton
@Provides
DbContract provideDatabase(DbRepository dbRepository) {
return dbRepository;
}
@Singleton
@Provides
SyncContract provideSync(SyncRepository syncRepository) {
return syncRepository;
}
@Provides
FirebaseJobDispatcher provideDispatcher() {
return new FirebaseJobDispatcher(new GooglePlayDriver(application));
}
}

View File

@ -1,103 +0,0 @@
package io.github.wulkanowy.di.modules;
import android.support.v4.app.Fragment;
import dagger.Module;
import dagger.Provides;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import io.github.wulkanowy.di.annotations.PerFragment;
import io.github.wulkanowy.ui.base.BasePagerAdapter;
import io.github.wulkanowy.ui.main.attendance.AttendanceContract;
import io.github.wulkanowy.ui.main.attendance.AttendanceHeaderItem;
import io.github.wulkanowy.ui.main.attendance.AttendancePresenter;
import io.github.wulkanowy.ui.main.attendance.AttendanceTabContract;
import io.github.wulkanowy.ui.main.attendance.AttendanceTabPresenter;
import io.github.wulkanowy.ui.main.exams.ExamsContract;
import io.github.wulkanowy.ui.main.exams.ExamsPresenter;
import io.github.wulkanowy.ui.main.exams.ExamsSubItem;
import io.github.wulkanowy.ui.main.exams.ExamsTabContract;
import io.github.wulkanowy.ui.main.exams.ExamsTabPresenter;
import io.github.wulkanowy.ui.main.grades.GradeHeaderItem;
import io.github.wulkanowy.ui.main.grades.GradesContract;
import io.github.wulkanowy.ui.main.grades.GradesPresenter;
import io.github.wulkanowy.ui.main.timetable.TimetableContract;
import io.github.wulkanowy.ui.main.timetable.TimetableHeaderItem;
import io.github.wulkanowy.ui.main.timetable.TimetablePresenter;
import io.github.wulkanowy.ui.main.timetable.TimetableTabContract;
import io.github.wulkanowy.ui.main.timetable.TimetableTabPresenter;
@Module
public class FragmentModule {
private final Fragment fragment;
public FragmentModule(Fragment fragment) {
this.fragment = fragment;
}
@PerFragment
@Provides
GradesContract.Presenter provideGradesPresenter(GradesPresenter gradesPresenter) {
return gradesPresenter;
}
@PerFragment
@Provides
AttendanceContract.Presenter provideAttendancePresenter(AttendancePresenter attendancePresenter) {
return attendancePresenter;
}
@PerFragment
@Provides
ExamsContract.Presenter provideDashboardPresenter(ExamsPresenter examsPresenter) {
return examsPresenter;
}
@PerFragment
@Provides
AttendanceTabContract.Presenter provideAttendanceTabPresenter(AttendanceTabPresenter timetableTabPresenter) {
return timetableTabPresenter;
}
@Provides
BasePagerAdapter provideBasePagerAdapter() {
return new BasePagerAdapter(fragment.getChildFragmentManager());
}
@Provides
FlexibleAdapter<AttendanceHeaderItem> provideAttendanceTabAdapter() {
return new FlexibleAdapter<>(null);
}
@Provides
FlexibleAdapter<TimetableHeaderItem> provideTimetableTabAdapter() {
return new FlexibleAdapter<>(null);
}
@Provides
FlexibleAdapter<GradeHeaderItem> provideGradesAdapter() {
return new FlexibleAdapter<>(null);
}
@Provides
FlexibleAdapter<ExamsSubItem> provideExamAdapter() {
return new FlexibleAdapter<>(null);
}
@PerFragment
@Provides
TimetableContract.Presenter provideTimetablePresenter(TimetablePresenter timetablePresenter) {
return timetablePresenter;
}
@PerFragment
@Provides
TimetableTabContract.Presenter provideTimetableTabPresenter(TimetableTabPresenter timetableTabPresenter) {
return timetableTabPresenter;
}
@Provides
ExamsTabContract.Presenter provideExamsTabPresenter(ExamsTabPresenter examsTabPresenter) {
return examsTabPresenter;
}
}

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.di.annotations;
package io.github.wulkanowy.di.scopes;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

View File

@ -1,11 +1,11 @@
package io.github.wulkanowy.di.annotations;
package io.github.wulkanowy.di.scopes;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
import javax.inject.Scope;
@Qualifier
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface DatabaseInfo {
public @interface PerChildFragment {
}

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.di.annotations;
package io.github.wulkanowy.di.scopes;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

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;
@ -20,13 +19,18 @@ import java.util.List;
import javax.inject.Inject;
import dagger.android.AndroidInjection;
import io.github.wulkanowy.R;
import io.github.wulkanowy.WulkanowyApp;
import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.sync.NotRegisteredUserException;
import io.github.wulkanowy.services.notifies.GradeNotify;
import io.github.wulkanowy.ui.main.MainActivity;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.FabricUtils;
import timber.log.Timber;
import static io.github.wulkanowy.utils.TimeUtilsKt.isHolidays;
public class SyncJob extends SimpleJobService {
@ -59,11 +63,17 @@ public class SyncJob extends SimpleJobService {
@Override
public void onCreate() {
super.onCreate();
((WulkanowyApp) getApplication()).getApplicationComponent().inject(this);
AndroidInjection.inject(this);
}
@Override
public int onRunJob(JobParameters job) {
if (isHolidays()) {
stop(getApplicationContext());
return JobService.RESULT_FAIL_NORETRY;
}
try {
repository.getSyncRepo().initLastUser();
repository.getSyncRepo().syncAll();
@ -73,10 +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) {
Crashlytics.logException(e);
LogUtils.error("During background synchronization an error occurred", e);
logError(e);
return JobService.RESULT_FAIL_RETRY;
}
}
@ -116,4 +140,9 @@ public class SyncJob extends SimpleJobService {
gradeList.size(), gradeList.size());
}
}
private void logError(Exception e) {
FabricUtils.logLogin("Background", false);
Timber.e(e, "During background synchronization an error occurred");
}
}

View File

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

View File

@ -1,35 +1,51 @@
package io.github.wulkanowy.ui.base;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatDelegate;
import android.widget.Toast;
import android.view.View;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import dagger.android.support.DaggerAppCompatActivity;
import io.github.wulkanowy.R;
import io.github.wulkanowy.WulkanowyApp;
import io.github.wulkanowy.di.component.ActivityComponent;
import io.github.wulkanowy.di.component.DaggerActivityComponent;
import io.github.wulkanowy.di.modules.ActivityModule;
import io.github.wulkanowy.utils.NetworkUtils;
public abstract class BaseActivity extends AppCompatActivity implements BaseContract.View {
private ActivityComponent activityComponent;
public abstract class BaseActivity extends DaggerAppCompatActivity implements BaseContract.View {
private Unbinder unbinder;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
activityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this))
.applicationComponent(((WulkanowyApp) getApplication()).getApplicationComponent())
.build();
protected void injectViews() {
unbinder = ButterKnife.bind(this);
}
@Override
public void showMessage(@NonNull String text) {
if (getMessageView() != null) {
Snackbar.make(getMessageView(), text, Snackbar.LENGTH_LONG).show();
}
}
@Override
public void showNoNetworkMessage() {
showMessage(getString(R.string.noInternet_text));
}
@Override
public boolean isNetworkConnected() {
return NetworkUtils.isOnline(getApplicationContext());
}
protected View getMessageView() {
return null;
}
@Override
@ -38,33 +54,6 @@ public abstract class BaseActivity extends AppCompatActivity implements BaseCont
if (unbinder != null) {
unbinder.unbind();
}
}
@Override
public void onError(int resId) {
onError(getString(resId));
}
@Override
public void onError(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
@Override
public void onNoNetworkError() {
onError(R.string.noInternet_text);
}
@Override
public boolean isNetworkConnected() {
return NetworkUtils.isOnline(getApplicationContext());
}
public ActivityComponent getActivityComponent() {
return activityComponent;
}
public void setButterKnife(Unbinder unbinder) {
this.unbinder = unbinder;
invalidateOptionsMenu();
}
}

View File

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

View File

@ -1,56 +1,20 @@
package io.github.wulkanowy.ui.base;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.annotation.StringRes;
import android.view.View;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import io.github.wulkanowy.R;
import io.github.wulkanowy.WulkanowyApp;
import io.github.wulkanowy.di.component.DaggerFragmentComponent;
import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.di.modules.FragmentModule;
import dagger.android.support.DaggerFragment;
import io.github.wulkanowy.utils.NetworkUtils;
public abstract class BaseFragment extends Fragment implements BaseContract.View {
private BaseActivity activity;
public abstract class BaseFragment extends DaggerFragment implements BaseContract.View {
private Unbinder unbinder;
private FragmentComponent fragmentComponent;
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof BaseActivity) {
activity = (BaseActivity) context;
}
fragmentComponent = DaggerFragmentComponent.builder()
.fragmentModule(new FragmentModule(this))
.applicationComponent(((WulkanowyApp) activity.getApplication()).getApplicationComponent())
.build();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setUpOnViewCreated(view);
}
@Override
public void onDetach() {
activity = null;
super.onDetach();
protected void injectViews(@NonNull View view) {
unbinder = ButterKnife.bind(this, view);
}
@Override
@ -61,44 +25,32 @@ public abstract class BaseFragment extends Fragment implements BaseContract.View
super.onDestroyView();
}
@Override
public void onError(int resId) {
onError(getString(resId));
}
@Override
public void onError(String message) {
if (activity != null) {
activity.onError(message);
public void setTitle(String title) {
if (getActivity() != null) {
getActivity().setTitle(title);
}
}
@Override
public void onNoNetworkError() {
onError(R.string.noInternet_text);
public void showMessage(@NonNull String text) {
if (getActivity() != null) {
((BaseActivity) getActivity()).showMessage(text);
}
}
public void showMessage(@StringRes int stringId) {
showMessage(getString(stringId));
}
@Override
public void showNoNetworkMessage() {
if (getActivity() != null) {
((BaseActivity) getActivity()).showNoNetworkMessage();
}
}
@Override
public boolean isNetworkConnected() {
return activity != null && activity.isNetworkConnected();
}
public void setButterKnife(Unbinder unbinder) {
this.unbinder = unbinder;
}
public void setTitle(String title) {
if (activity != null) {
activity.setTitle(title);
}
}
public FragmentComponent getFragmentComponent() {
return fragmentComponent;
}
protected void setUpOnViewCreated(View fragmentView) {
// do something on view created
return NetworkUtils.isOnline(getContext());
}
}

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