1
0

Compare commits

..

32 Commits
0.1.0 ... 0.3.1

Author SHA1 Message Date
6927ac0e4f Version 0.3.1 2018-04-20 20:39:18 +02:00
2fd5b0f6ee [API] Improve login (#83) 2018-04-20 20:30:39 +02:00
b7a6b71a4d Change message after sync completed (#85) 2018-04-20 14:07:40 +02:00
ce00c7025a Version 0.3.0 2018-04-08 20:16:52 +02:00
5c558ae1f9 Show fully name on expand header (#82) 2018-04-08 18:05:41 +02:00
0708d84b98 Log non-fatal exceptions with fabric (#80) 2018-04-08 17:34:18 +02:00
b22d95392b Update CI config (#81) 2018-04-08 17:14:59 +02:00
78d57ca746 Notifications improvements (#78)
* Refactor notifications
* Change grade notify icon
2018-04-08 16:37:27 +02:00
0aa8c5605d Add setttings (#74) 2018-04-08 16:15:05 +02:00
cb6afb137f Hide actionbar during login (#79) 2018-04-08 15:54:46 +02:00
c72e7748e2 Migrate to ThreeTenABP (#77) 2018-04-05 21:56:08 +02:00
5c9f10fa50 Update gradle to 4.4 (#75) 2018-04-03 16:10:56 +02:00
cdd3d6a53b Version 0.2.2 2018-03-22 22:54:11 +01:00
8431661d54 Fix resuming states in fragments (#72)
* Downgrade sdk version
* Update dependencies
* Fix infinite loading on restore states
* Fix crash when loading loaded fragments
2018-03-22 22:42:08 +01:00
3dabb11473 [API] Add error messages to exceptions (#71) 2018-03-20 23:10:20 +01:00
63b4ed42ca Version 0.2.1 2018-03-14 22:08:57 +01:00
2bb2341d0f Fix display items on refresh (#70) 2018-03-14 21:56:39 +01:00
797e233809 Fix inactive elements (#69) 2018-03-14 20:22:31 +01:00
64b964ca18 Fix circleci instrumented job (#68) 2018-03-13 22:30:33 +01:00
c9bdac6a66 Fix session handling (#67) 2018-03-13 18:19:04 +01:00
83b4f359bc Version 0.2.0 2018-03-11 20:01:45 +01:00
a094d4f0d9 Fix crash in grades on some devices (#66) 2018-03-11 19:31:56 +01:00
3aca34340d Optimize session handling (#63)
* [APP] Change way the Vulcan is configured (#65)
2018-03-11 19:16:20 +01:00
a0313827ce [DB] Add database migrations (#64) 2018-03-11 18:18:33 +01:00
3799fa910b Add attendance (#58) 2018-03-10 15:55:34 +01:00
828e76821b Replace versioneye with snyk (#62) 2018-03-10 14:04:00 +01:00
e050982de2 Add application id suffix in debug builds (#61) 2018-03-10 13:08:01 +01:00
69fc4bf874 Add cleaning user data on upgrade and downgrade (#60) 2018-03-06 20:14:54 +01:00
c3803b1c96 Add a view of free days to the timetable (#57)
* Fix Timetable freeze when app was killed
* Add views for free days
2018-03-06 00:57:06 +01:00
e274949257 Add remove user id in db upgrade (#59) 2018-03-06 00:25:12 +01:00
bc4b150858 Version 0.1.1 2018-03-04 22:07:55 +01:00
e187493e01 CustomTabs fix (#56) 2018-03-04 20:27:44 +01:00
157 changed files with 4722 additions and 1298 deletions

View File

@ -7,7 +7,7 @@ references:
container_config: &container_config
docker:
- image: circleci/android:api-27-alpha
- image: circleci/android:api-26-alpha
working_directory: *workspace_root
environment:
environment:
@ -126,6 +126,9 @@ jobs:
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
background: true
- run:
name: Change circle-android script file permissions
command: sudo chmod +rx /bin/circle-android
- run:
name: Wait emulator
command: |

2
.gitignore vendored
View File

@ -32,6 +32,8 @@ local.properties
.idea/tasks.xml
.idea/vcs.xml
.idea/workspace.xml
.idea/caches/
.idea/codeStyles/
*.iml
# OS-specific files

View File

@ -12,7 +12,7 @@ build:
script:
- ./gradlew --no-daemon --stacktrace dependencies || true
- ./gradlew --no-daemon --stacktrace assembleDebug
- mv app/build/outputs/apk/app-debug.apk .
- mv app/build/outputs/apk/debug/app-debug.apk .
artifacts:
name: "${CI_PROJECT_NAME}_${CI_BUILD_REF_NAME}-${CI_BUILD_ID}"
paths:
@ -26,7 +26,7 @@ tests:
- .gradle
policy: pull
script:
- ./gradlew --no-daemon --stacktrace test
- ./gradlew --no-daemon --stacktrace -x fabricGenerateResourcesRelease test
artifacts:
paths:
- app/build/reports/tests
@ -39,7 +39,7 @@ lint:
- .gradle
policy: pull
script:
- ./gradlew --no-daemon --stacktrace lint
- ./gradlew --no-daemon --stacktrace -x fabricGenerateResourcesRelease lint
artifacts:
paths:
- app/build/reports

View File

@ -1,12 +1,13 @@
# Wulkanowy
[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy)
[![Dependency Status](https://www.versioneye.com/user/projects/5969ff0b0fb24f004f8c711b/badge.svg?style=flat-square)](https://www.versioneye.com/user/projects/5969ff0b0fb24f004f8c711b)
[![CircleCI](https://img.shields.io/circleci/project/github/wulkanowy/wulkanowy.svg?style=flat-square)](https://circleci.com/gh/wulkanowy/wulkanowy)
[![CircleCI](https://img.shields.io/circleci/project/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://circleci.com/gh/wulkanowy/wulkanowy)
[![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)
[![Bintray](https://img.shields.io/bintray/v/wulkanowy/wulkanowy/api.svg?style=flat-square)](https://bintray.com/wulkanowy/wulkanowy/api)
[Pobierz wersję rozwojową](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/app-release-bitrise-signed.apk)
[Pobierz wersję rozwojową](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/app-debug-bitrise-signed.apk)
Wulkanowy to projekt aplikacji na androida ułatwiającej używanie dziennika VULCANa.
Androidowy klient dziennika VULCAN UONET+.

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-27/android.jar," +
def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-26/android.jar," +
"${project.rootDir}/api/build/libs/" + files[0]
property "sonar.projectName", GROUP_ID + ":app"

View File

@ -28,12 +28,12 @@ jacocoTestReport {
}
dependencies {
implementation 'org.jsoup:jsoup:1.10.3'
implementation 'org.apache.commons:commons-lang3:3.7'
implementation 'com.google.code.gson:gson:2.8.2'
implementation "org.jsoup:jsoup:$jsoup"
implementation "org.apache.commons:commons-lang3:$apacheLang"
implementation "com.google.code.gson:gson:$gson"
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.13.0'
testImplementation "junit:junit:$junit"
testImplementation "org.mockito:mockito-core:$mockito"
}
version = PUBLISH_VERSION

View File

@ -5,44 +5,109 @@ import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import io.github.wulkanowy.api.login.Login;
public class Client {
private String protocol;
private String protocol = "https";
private String host;
private String host = "vulcan.net.pl";
private String email;
private String password;
private String symbol;
private Date lastSuccessRequest = null;
private Cookies cookies = new Cookies();
Client(String protocol, String host, String symbol) {
this.protocol = protocol;
this.host = host;
Client(String email, String password, String symbol) {
this.email = email;
this.password = password;
this.symbol = symbol;
setFullEndpointInfo(email);
}
String getHost() {
return host;
private void setFullEndpointInfo(String info) {
String[] creds = info.split("\\\\");
email = info;
if (creds.length > 2) {
String[] url = creds[0].split("://");
protocol = url[0];
host = url[1];
email = creds[2];
}
}
private void login() throws IOException, VulcanException {
if (isLoggedIn()) {
return;
}
this.cookies = new Cookies();
this.symbol = new Login(this).login(email, password, symbol);
}
private boolean isLoggedIn() {
return getCookies().size() > 0 && lastSuccessRequest != null &&
29 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime());
}
public String getSymbol() {
return symbol;
}
public void setSymbol(String symbol) {
this.symbol = symbol;
}
Map<String, String> getCookies() {
public void addCookies(Map<String, String> items) {
cookies.addItems(items);
}
private Map<String, String> getCookies() {
return cookies.getItems();
}
private String getFilledUrl(String url) {
String getHost() {
return host;
}
String getFilledUrl(String url) {
return url
.replace("{schema}", protocol)
.replace("{host}", host.replace(":", "%253A"))
.replace("{symbol}", symbol == null ? "Default" : symbol);
.replace("{symbol}", symbol);
}
Document getPageByUrl(String url) throws IOException {
public Document getPageByUrl(String url) throws IOException, VulcanException {
return getPageByUrl(url, true, null);
}
public Document getPageByUrl(String url, boolean loginBefore) throws IOException, VulcanException {
return getPageByUrl(url, loginBefore, null);
}
public Document getPageByUrl(String url, boolean loginBefore, Map<String, String> cookies) throws IOException, VulcanException {
if (loginBefore) {
login();
}
if (null != cookies) {
this.cookies.addItems(cookies);
}
Connection.Response response = Jsoup.connect(getFilledUrl(url))
.followRedirects(true)
.cookies(getCookies())
@ -50,10 +115,16 @@ public class Client {
this.cookies.addItems(response.cookies());
return response.parse();
Document doc = checkForErrors(response.parse());
if (loginBefore) {
lastSuccessRequest = new Date();
}
return doc;
}
public Document postPageByUrl(String url, String[][] params) throws IOException {
public Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException {
Connection connection = Jsoup.connect(getFilledUrl(url));
for (String[] data : params) {
@ -68,10 +139,12 @@ public class Client {
this.cookies.addItems(response.cookies());
return response.parse();
return checkForErrors(response.parse());
}
public String getJsonStringByUrl(String url) throws IOException {
public String getJsonStringByUrl(String url) throws IOException, VulcanException {
login();
Connection.Response response = Jsoup.connect(getFilledUrl(url))
.followRedirects(true)
.ignoreContentType(true)
@ -83,7 +156,9 @@ public class Client {
return response.body();
}
public String postJsonStringByUrl(String url, String[][] params) throws IOException {
public String postJsonStringByUrl(String url, String[][] params) throws IOException, VulcanException {
login();
Connection connection = Jsoup.connect(getFilledUrl(url));
for (String[] data : params) {
@ -101,4 +176,24 @@ public class Client {
return response.body();
}
Document checkForErrors(Document doc) throws VulcanException {
lastSuccessRequest = null;
String title = doc.select("title").text();
if ("Przerwa techniczna".equals(title)) {
throw new VulcanOfflineException(title);
}
String singIn = doc.select(".loginButton").text();
if ("Zaloguj się".equals(singIn)) {
throw new NotLoggedInErrorException(singIn);
}
if ("Błąd strony".equals(title)) {
throw new VulcanException("Nieznany błąd");
}
return doc;
}
}

View File

@ -3,21 +3,15 @@ package io.github.wulkanowy.api;
import java.util.HashMap;
import java.util.Map;
public class Cookies {
class Cookies {
private Map<String, String> jar = new HashMap<>();
public Map<String, String> getItems() {
Map<String, String> getItems() {
return jar;
}
public Cookies setItems(Map<String, String> items) {
this.jar = items;
return this;
}
public Cookies addItems(Map<String, String> items) {
this.jar.putAll(items);
return this;
void addItems(Map<String, String> items) {
jar.putAll(items);
}
}

View File

@ -0,0 +1,50 @@
package io.github.wulkanowy.api;
public class Diary implements ParamItem {
private String id = "";
private String studentId = "";
private String name = "";
private boolean current = false;
public String getId() {
return id;
}
public Diary setId(String id) {
this.id = id;
return this;
}
public String getStudentId() {
return studentId;
}
@Override
public Diary setStudentId(String studentId) {
this.studentId = studentId;
return this;
}
public String getName() {
return name;
}
public Diary setName(String name) {
this.name = name;
return this;
}
@Override
public boolean isCurrent() {
return current;
}
public Diary setCurrent(boolean current) {
this.current = current;
return this;
}
}

View File

@ -0,0 +1,8 @@
package io.github.wulkanowy.api;
public class NotLoggedInErrorException extends VulcanException {
public NotLoggedInErrorException(String message) {
super(message);
}
}

View File

@ -0,0 +1,14 @@
package io.github.wulkanowy.api;
interface ParamItem {
ParamItem setId(String id);
ParamItem setStudentId(String id);
ParamItem setName(String name);
ParamItem setCurrent(boolean isCurrent);
boolean isCurrent();
}

View File

@ -1,21 +1,14 @@
package io.github.wulkanowy.api;
public class Semester {
private String number = "";
public class Semester implements ParamItem {
private String id = "";
private boolean isCurrent = false;
private String studentId = "";
public String getNumber() {
return number;
}
private String name = "";
public Semester setNumber(String number) {
this.number = number;
return this;
}
private boolean current = false;
public String getId() {
return id;
@ -26,12 +19,31 @@ public class Semester {
return this;
}
public String getStudentId() {
return studentId;
}
@Override
public Semester setStudentId(String studentId) {
this.studentId = studentId;
return this;
}
public String getName() {
return name;
}
public Semester setName(String number) {
this.name = number;
return this;
}
public boolean isCurrent() {
return isCurrent;
return current;
}
public Semester setCurrent(boolean current) {
isCurrent = current;
this.current = current;
return this;
}
}

View File

@ -6,21 +6,27 @@ import org.jsoup.nodes.Element;
import java.io.IOException;
import java.util.List;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
public interface SnP {
String getId();
String getSchoolID();
void storeContextCookies() throws IOException, NotLoggedInErrorException;
void setDiaryID(String id);
String getStudentID();
List<Student> getStudents() throws IOException, VulcanException;
StudentAndParent setUp() throws IOException, VulcanException;
String getRowDataChildValue(Element e, int index);
Document getSnPPageDocument(String url) throws IOException;
Document getSnPPageDocument(String url) throws IOException, VulcanException;
List<Semester> getSemesters() throws IOException;
List<Diary> getDiaries() throws IOException, VulcanException;
List<Semester> getSemesters() throws IOException, VulcanException;
List<Semester> getSemesters(Document gradesPage);
Semester getCurrentSemester(List<Semester> semesterList);
<T> T getCurrent(List<? extends ParamItem> list);
}

View File

@ -0,0 +1,46 @@
package io.github.wulkanowy.api;
public class Student implements ParamItem {
private String id = "";
private String name = "";
private boolean current = false;
public String getId() {
return id;
}
public Student setId(String id) {
this.id = id;
return this;
}
public String getStudentId() {
return getId();
}
@Override
public Student setStudentId(String studentId) {
return setId(studentId);
}
public String getName() {
return name;
}
public Student setName(String name) {
this.name = name;
return this;
}
public boolean isCurrent() {
return current;
}
public Student setCurrent(boolean current) {
this.current = current;
return this;
}
}

View File

@ -5,10 +5,11 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import java.util.Map;
public class StudentAndParent implements SnP {
@ -20,31 +21,47 @@ public class StudentAndParent implements SnP {
private Client client;
private String id;
private String schoolID;
StudentAndParent(Client client) {
private String studentID;
private String diaryID;
StudentAndParent(Client client, String schoolID, String studentID, String diaryID) {
this.client = client;
this.schoolID = schoolID;
this.studentID = studentID;
this.diaryID = diaryID;
}
StudentAndParent(Client client, String id) {
this(client);
this.id = id;
public StudentAndParent setUp() throws IOException, VulcanException {
if (null == getStudentID() || "".equals(getStudentID())) {
Document doc = client.getPageByUrl(getSnpHomePageUrl());
Student student = getCurrent(getStudents(doc));
studentID = student.getId();
Diary diary = getCurrent(getDiaries(doc));
diaryID = diary.getId();
}
return this;
}
public String getSchoolID() {
return schoolID;
}
public String getStudentID() {
return studentID;
}
private String getBaseUrl() {
return BASE_URL.replace("{ID}", getId());
return BASE_URL.replace("{ID}", getSchoolID());
}
public String getId() {
return id;
}
public void storeContextCookies() throws IOException, NotLoggedInErrorException {
client.getPageByUrl(getSnpHomePageUrl());
}
String getSnpHomePageUrl() throws IOException, NotLoggedInErrorException {
if (null != getId()) {
String getSnpHomePageUrl() throws IOException, VulcanException {
if (null != getSchoolID()) {
return getBaseUrl();
}
@ -53,12 +70,12 @@ public class StudentAndParent implements SnP {
Element studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a").first();
if (null == studentTileLink) {
throw new NotLoggedInErrorException();
throw new NotLoggedInErrorException("You are probably not logged in. Force login");
}
String snpPageUrl = studentTileLink.attr("href");
this.id = getExtractedIdFromUrl(snpPageUrl);
this.schoolID = getExtractedIdFromUrl(snpPageUrl);
return snpPageUrl;
}
@ -67,7 +84,7 @@ public class StudentAndParent implements SnP {
String[] path = snpPageUrl.split(client.getHost())[1].split("/");
if (5 != path.length) {
throw new NotLoggedInErrorException();
throw new NotLoggedInErrorException("You are probably not logged in");
}
return path[2];
@ -77,11 +94,42 @@ public class StudentAndParent implements SnP {
return e.select(".daneWiersz .wartosc").get(index - 1).text();
}
public Document getSnPPageDocument(String url) throws IOException {
return client.getPageByUrl(getBaseUrl() + url);
public void setDiaryID(String id) {
this.diaryID = id;
}
public List<Semester> getSemesters() throws IOException {
public Document getSnPPageDocument(String url) throws IOException, VulcanException {
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);
if ("Witryna ucznia i rodzica Strona główna".equals(doc.select("title").first().text())) {
throw new VulcanException("Sesja została nieprawidłowo zainicjowana");
}
return doc;
}
public List<Diary> getDiaries() throws IOException, VulcanException {
return getDiaries(client.getPageByUrl(getBaseUrl()));
}
private List<Diary> getDiaries(Document doc) throws IOException, VulcanException {
return getList(doc.select("#dziennikDropDownList option"), Diary.class);
}
public List<Student> getStudents() throws IOException, VulcanException {
return getStudents(client.getPageByUrl(getBaseUrl()));
}
private List<Student> getStudents(Document doc) throws IOException, VulcanException {
return getList(doc.select("#uczenDropDownList option"), Student.class);
}
public List<Semester> getSemesters() throws IOException, VulcanException {
return getSemesters(getSnPPageDocument(GRADES_PAGE_URL));
}
@ -93,9 +141,9 @@ public class StudentAndParent implements SnP {
for (Element e : semesterOptions) {
Semester semester = new Semester()
.setId(e.text())
.setNumber(e.attr("value"));
.setName(e.attr("value"));
if ("selected".equals(e.attr("selected"))) {
if (isCurrent(e)) {
semester.setCurrent(true);
}
@ -105,15 +153,47 @@ public class StudentAndParent implements SnP {
return semesters;
}
public Semester getCurrentSemester(List<Semester> semesterList) {
Semester current = null;
for (Semester s : semesterList) {
@SuppressWarnings("unchecked")
private <T> List<T> getList(Elements options, Class<? extends ParamItem> type) throws IOException, VulcanException {
List<T> list = new ArrayList<>();
for (Element e : options) {
URL url = new URL(e.val());
try {
ParamItem item = type.newInstance()
.setId(url.getQuery().split("=")[1])
.setName(e.text());
if (isCurrent(e)) {
item.setCurrent(true);
}
if (item instanceof Diary) {
item.setStudentId(getStudentID());
}
list.add((T) item);
} catch (Exception ex) {
throw new VulcanException("Error while trying to parse params list", ex);
}
}
return list;
}
@SuppressWarnings("unchecked")
public <T> T getCurrent(List<? extends ParamItem> list) {
ParamItem current = null;
for (ParamItem s : list) {
if (s.isCurrent()) {
current = s;
break;
}
}
return current;
return (T) current;
}
private boolean isCurrent(Element e) {
return "selected".equals(e.attr("selected"));
}
}

View File

@ -7,12 +7,6 @@ import io.github.wulkanowy.api.attendance.AttendanceTable;
import io.github.wulkanowy.api.exams.ExamsWeek;
import io.github.wulkanowy.api.grades.GradesList;
import io.github.wulkanowy.api.grades.SubjectsList;
import io.github.wulkanowy.api.login.AccountPermissionException;
import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.api.login.Login;
import io.github.wulkanowy.api.login.LoginErrorException;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException;
import io.github.wulkanowy.api.messages.Messages;
import io.github.wulkanowy.api.notes.AchievementsList;
import io.github.wulkanowy.api.notes.NotesList;
@ -24,173 +18,97 @@ import io.github.wulkanowy.api.user.FamilyInformation;
public class Vulcan {
private String id;
private String symbol;
private SnP snp;
private String protocolSchema = "https";
private String logHost = "vulcan.net.pl";
private String email;
private Client client;
private Login login;
private String schoolId;
public void setClient(Client client) {
this.client = client;
private String studentId;
private String diaryId;
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);
}
public void setLogin(Login login) {
this.login = login;
}
public void login(String email, String password, String symbol)
throws BadCredentialsException, AccountPermissionException,
LoginErrorException, IOException, VulcanOfflineException {
setFullEndpointInfo(email);
login = getLogin();
this.symbol = login.login(this.email, password, symbol);
}
public Vulcan login(String email, String password, String symbol, String id)
throws BadCredentialsException, AccountPermissionException,
LoginErrorException, IOException, VulcanOfflineException {
login(email, password, symbol);
this.id = id;
return this;
}
String getProtocolSchema() {
return protocolSchema;
}
String getLogHost() {
return logHost;
}
public String getEmail() {
return email;
}
public String getSymbol() {
return symbol;
}
private void setFullEndpointInfo(String email) {
String[] creds = email.split("\\\\");
this.email = email;
if (creds.length >= 2) {
String[] url = creds[0].split("://");
this.protocolSchema = url[0];
this.logHost = url[1];
this.email = creds[2];
public Client getClient() throws NotLoggedInErrorException {
if (null == client) {
throw new NotLoggedInErrorException("Use setCredentials() method first");
}
}
protected Client getClient() {
if (null != client) {
return client;
}
client = new Client(getProtocolSchema(), getLogHost(), symbol);
return client;
}
protected Login getLogin() {
if (null != login) {
return login;
}
public String getSymbol() throws NotLoggedInErrorException {
return getClient().getSymbol();
login = new Login(getClient());
return login;
}
public SnP getStudentAndParent() throws IOException, NotLoggedInErrorException {
if (0 == getClient().getCookies().size()) {
throw new NotLoggedInErrorException();
public SnP getStudentAndParent() throws VulcanException, IOException {
if (null != this.snp) {
return this.snp;
}
if (null != snp) {
return snp;
}
this.snp = new StudentAndParent(getClient(), schoolId, studentId, diaryId)
.setUp();
snp = createSnp(getClient(), id);
snp.storeContextCookies();
return snp;
return this.snp;
}
SnP createSnp(Client client, String id) {
if (null == id) {
return new StudentAndParent(client);
}
return new StudentAndParent(client, id);
}
public AttendanceStatistics getAttendanceStatistics() throws IOException, NotLoggedInErrorException {
return new AttendanceStatistics(getStudentAndParent());
}
public AttendanceTable getAttendanceTable() throws IOException, NotLoggedInErrorException {
public AttendanceTable getAttendanceTable() throws IOException, VulcanException {
return new AttendanceTable(getStudentAndParent());
}
public ExamsWeek getExamsList() throws IOException, NotLoggedInErrorException {
public AttendanceStatistics getAttendanceStatistics() throws IOException, VulcanException {
return new AttendanceStatistics(getStudentAndParent());
}
public ExamsWeek getExamsList() throws IOException, VulcanException {
return new ExamsWeek(getStudentAndParent());
}
public GradesList getGradesList() throws IOException, NotLoggedInErrorException {
public GradesList getGradesList() throws IOException, VulcanException {
return new GradesList(getStudentAndParent());
}
public SubjectsList getSubjectsList() throws IOException, NotLoggedInErrorException {
public SubjectsList getSubjectsList() throws IOException, VulcanException {
return new SubjectsList(getStudentAndParent());
}
public AchievementsList getAchievementsList() throws IOException, NotLoggedInErrorException {
public AchievementsList getAchievementsList() throws IOException, VulcanException {
return new AchievementsList(getStudentAndParent());
}
public NotesList getNotesList() throws IOException, NotLoggedInErrorException {
public NotesList getNotesList() throws IOException, VulcanException {
return new NotesList(getStudentAndParent());
}
public SchoolInfo getSchoolInfo() throws IOException, NotLoggedInErrorException {
public SchoolInfo getSchoolInfo() throws IOException, VulcanException {
return new SchoolInfo(getStudentAndParent());
}
public TeachersInfo getTeachersInfo() throws IOException, NotLoggedInErrorException {
public TeachersInfo getTeachersInfo() throws IOException, VulcanException {
return new TeachersInfo(getStudentAndParent());
}
public Timetable getTimetable() throws IOException, NotLoggedInErrorException {
public Timetable getTimetable() throws IOException, VulcanException {
return new Timetable(getStudentAndParent());
}
public BasicInformation getBasicInformation() throws IOException, NotLoggedInErrorException {
public BasicInformation getBasicInformation() throws IOException, VulcanException {
return new BasicInformation(getStudentAndParent());
}
public FamilyInformation getFamilyInformation() throws IOException, NotLoggedInErrorException {
public FamilyInformation getFamilyInformation() throws IOException, VulcanException {
return new FamilyInformation(getStudentAndParent());
}
public Messages getMessages() {
public Messages getMessages() throws VulcanException {
return new Messages(getClient());
}
}

View File

@ -0,0 +1,12 @@
package io.github.wulkanowy.api;
public class VulcanException extends Exception {
protected VulcanException(String message) {
super(message);
}
protected VulcanException(String message, Exception e) {
super(message, e);
}
}

View File

@ -0,0 +1,8 @@
package io.github.wulkanowy.api;
public class VulcanOfflineException extends VulcanException {
VulcanOfflineException(String message) {
super(message);
}
}

View File

@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.api.generic.Month;
import io.github.wulkanowy.api.generic.Subject;
@ -22,15 +23,15 @@ public class AttendanceStatistics {
this.snp = snp;
}
public Types getTypesTable() throws IOException {
public Types getTypesTable() throws IOException, VulcanException {
return getTypesTable("");
}
public Types getTypesTable(String tick) throws IOException {
public Types getTypesTable(String tick) throws IOException, VulcanException {
return getTypesTable(tick, -1);
}
public List<Subject> getSubjectList() throws IOException {
public List<Subject> getSubjectList() throws IOException, VulcanException {
Element mainContainer = snp.getSnPPageDocument(attendancePageUrl)
.select(".mainContainer #idPrzedmiot").first();
@ -46,7 +47,7 @@ public class AttendanceStatistics {
return subjectList;
}
public Types getTypesTable(String tick, Integer subjectId) throws IOException {
public Types getTypesTable(String tick, Integer subjectId) throws IOException, VulcanException {
Element mainContainer = snp.getSnPPageDocument((attendancePageUrl
+ "?data={tick}&idPrzedmiot={subject}")
.replace("{tick}", tick)

View File

@ -4,37 +4,52 @@ 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.Day;
import io.github.wulkanowy.api.generic.Lesson;
import io.github.wulkanowy.api.generic.Week;
public class AttendanceTable {
private SnP snp;
private final static String ATTENDANCE_PAGE_URL = "Frekwencja.mvc?data=";
private String attendancePageUrl = "Frekwencja.mvc?data=";
private SnP snp;
public AttendanceTable(SnP snp) {
this.snp = snp;
}
public Week<Day> getWeekTable() throws IOException {
public Week<Day> getWeekTable() throws IOException, ParseException, VulcanException {
return getWeekTable("");
}
public Week<Day> getWeekTable(String tick) throws IOException {
Element table = snp.getSnPPageDocument(attendancePageUrl + tick)
public Week<Day> getWeekTable(String tick) throws IOException, ParseException, VulcanException {
Element table = snp.getSnPPageDocument(ATTENDANCE_PAGE_URL + tick)
.select(".mainContainer .presentData").first();
Elements headerCells = table.select("thead th");
List<Day> days = new ArrayList<>();
for (int i = 1; i < headerCells.size(); i++) {
days.add(new Day().setDate(headerCells.get(i).html().split("<br>")[1]));
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);
}
Elements hoursInDays = table.select("tbody tr");
@ -46,26 +61,29 @@ public class AttendanceTable {
// fill hours in day
int size = hours.size();
for (int i = 1; i < size; i++) {
days.get(i - 1).setLesson(getNewLesson(hours.get(i)));
Lesson lesson = new Lesson();
lesson.setDate(days.get(i - 1).getDate());
lesson.setNumber(hours.get(0).text());
addLessonDetails(lesson, hours.get(i));
days.get(i - 1).setLesson(lesson);
}
}
String[] dayDescription = headerCells.get(1).html().split("<br>");
return new Week<Day>()
.setStartDayDate(dayDescription[1])
.setStartDayDate(days.get(0).getDate())
.setDays(days);
}
private Lesson getNewLesson(Element cell) {
Lesson lesson = new Lesson();
private void addLessonDetails(Lesson lesson, Element cell) {
lesson.setSubject(cell.select("span").text());
if (LessonTypes.CLASS_NOT_EXIST.equals(cell.attr("class"))) {
lesson.setNotExist(true);
lesson.setEmpty(true);
return lesson;
return;
}
switch (cell.select("div").attr("class")) {
@ -95,7 +113,5 @@ public class AttendanceTable {
lesson.setEmpty(true);
break;
}
return lesson;
}
}

View File

@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.api.generic.Week;
public class ExamsWeek {
@ -21,11 +22,11 @@ public class ExamsWeek {
this.snp = snp;
}
public Week<ExamDay> getCurrent() throws IOException {
public Week<ExamDay> getCurrent() throws IOException, VulcanException {
return getWeek("", true);
}
public Week<ExamDay> getWeek(String tick, final boolean onlyNotEmpty) throws IOException {
public Week<ExamDay> getWeek(String tick, final boolean onlyNotEmpty) throws IOException, VulcanException {
Document examsPage = snp.getSnPPageDocument(EXAMS_PAGE_URL + tick);
Elements examsDays = examsPage.select(".mainContainer > div:not(.navigation)");

View File

@ -16,12 +16,13 @@ import java.util.regex.Pattern;
import io.github.wulkanowy.api.Semester;
import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException;
public class GradesList {
private static final String GRADES_PAGE_URL = "Oceny/Wszystkie?details=2&okres=";
private SnP snp = null;
private SnP snp;
private List<Grade> grades = new ArrayList<>();
@ -33,50 +34,68 @@ public class GradesList {
return GRADES_PAGE_URL;
}
public List<Grade> getAll() throws IOException, ParseException {
public List<Grade> getAll() throws IOException, ParseException, VulcanException {
return getAll("");
}
public List<Grade> getAll(String semester) throws IOException, ParseException {
public List<Grade> getAll(String semester) throws IOException, ParseException, VulcanException {
Document gradesPage = snp.getSnPPageDocument(getGradesPageUrl() + semester);
Elements gradesRows = gradesPage.select(".ocenySzczegoly-table > tbody > tr");
Semester currentSemester = snp.getCurrentSemester(snp.getSemesters(gradesPage));
if ("".equals(semester)) {
List<Semester> semesterList = snp.getSemesters(gradesPage);
Semester currentSemester = snp.getCurrent(semesterList);
semester = currentSemester.getName();
}
for (Element row : gradesRows) {
if ("Brak ocen".equals(row.select("td:nth-child(2)").text())) {
continue;
}
String descriptions = row.select("td:nth-child(3)").text();
String symbol = descriptions.split(", ")[0];
String description = descriptions.replaceFirst(symbol, "").replaceFirst(", ", "");
Pattern pattern = Pattern.compile("#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})");
Matcher matcher = pattern.matcher(row.select("td:nth-child(2) span.ocenaCzastkowa")
.attr("style"));
String color = "";
while (matcher.find()) {
color = matcher.group(1);
}
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT);
Date d = sdf.parse(row.select("td:nth-child(5)").text());
sdf.applyPattern("yyyy-MM-dd");
grades.add(new Grade()
.setSubject(row.select("td:nth-child(1)").text())
.setValue(row.select("td:nth-child(2)").text())
.setColor(color)
.setSymbol(symbol)
.setDescription(description)
.setWeight(row.select("td:nth-child(4)").text())
.setDate(sdf.format(d))
.setTeacher(row.select("td:nth-child(6)").text())
.setSemester(currentSemester.getNumber())
);
grades.add(getGrade(row, semester));
}
return grades;
}
private Grade getGrade(Element row, String semester) throws ParseException {
String descriptions = row.select("td:nth-child(3)").text();
String symbol = descriptions.split(", ")[0];
String description = descriptions.replaceFirst(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());
return new Grade()
.setSubject(row.select("td:nth-child(1)").text())
.setValue(row.select("td:nth-child(2)").text())
.setColor(color)
.setSymbol(symbol)
.setDescription(description)
.setWeight(row.select("td:nth-child(4)").text())
.setDate(date)
.setTeacher(row.select("td:nth-child(6)").text())
.setSemester(semester);
}
private String getColor(String styleAttr) {
Pattern pattern = Pattern.compile("#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})");
Matcher matcher = pattern.matcher(styleAttr);
String color = "";
while (matcher.find()) {
color = matcher.group(1);
}
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

@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException;
public class SubjectsList {
@ -20,7 +21,7 @@ public class SubjectsList {
this.snp = snp;
}
public List<Subject> getAll() throws IOException {
public List<Subject> getAll() throws IOException, VulcanException {
Document subjectPage = snp.getSnPPageDocument(SUBJECTS_PAGE_URL);
Elements rows = subjectPage.select(".ocenyZwykle-table > tbody > tr");

View File

@ -1,4 +1,10 @@
package io.github.wulkanowy.api.login;
public class AccountPermissionException extends Exception {
import io.github.wulkanowy.api.VulcanException;
public class AccountPermissionException extends VulcanException {
AccountPermissionException(String message) {
super(message);
}
}

View File

@ -1,4 +1,10 @@
package io.github.wulkanowy.api.login;
public class BadCredentialsException extends Exception {
import io.github.wulkanowy.api.VulcanException;
public class BadCredentialsException extends VulcanException {
BadCredentialsException(String message) {
super(message);
}
}

View File

@ -2,12 +2,14 @@ package io.github.wulkanowy.api.login;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
import java.io.IOException;
import io.github.wulkanowy.api.Client;
import io.github.wulkanowy.api.VulcanException;
public class Login {
@ -16,64 +18,60 @@ public class Login {
"{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx%26wctx%3D" +
"{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx";
private static final String LOGIN_ENDPOINT_PAGE_URL =
"{schema}://uonetplus.{host}/{symbol}/LoginEndpoint.aspx";
private Client client;
private String symbol;
public Login(Client client) {
this.client = client;
}
public String login(String email, String password, String symbol)
throws BadCredentialsException, LoginErrorException,
AccountPermissionException, IOException, VulcanOfflineException {
String certificate = sendCredentials(email, password, symbol);
public String login(String email, String password, String symbol) throws VulcanException, IOException {
Document certDoc = sendCredentials(email, password);
return sendCertificate(certificate, symbol);
return sendCertificate(certDoc, symbol);
}
String sendCredentials(String email, String password, String symbol)
throws IOException, BadCredentialsException {
this.symbol = symbol;
Document sendCredentials(String email, String password) throws IOException, VulcanException {
Document html = client.postPageByUrl(LOGIN_PAGE_URL, new String[][]{
{"LoginName", email},
{"Password", password}
});
if (null != html.select(".ErrorMessage").first()) {
throw new BadCredentialsException();
Element errorMessage = html.select(".ErrorMessage").first();
if (null != errorMessage) {
throw new BadCredentialsException(errorMessage.text());
}
return html.select("input[name=wresult]").attr("value");
return html;
}
String sendCertificate(String certificate, String defaultSymbol)
throws IOException, LoginErrorException, AccountPermissionException, VulcanOfflineException {
this.symbol = findSymbol(defaultSymbol, certificate);
client.setSymbol(this.symbol);
String sendCertificate(Document doc, String defaultSymbol) throws IOException, VulcanException {
String certificate = doc.select("input[name=wresult]").val();
Document html = client.postPageByUrl(LOGIN_ENDPOINT_PAGE_URL, new String[][]{
String symbol = findSymbol(defaultSymbol, certificate);
client.setSymbol(symbol);
Document targetDoc = sendCertData(doc);
String title = targetDoc.select("title").text();
if ("Logowanie".equals(title)) {
throw new AccountPermissionException("No account access. Try another symbol");
}
if (!"Uonet+".equals(title)) {
throw new LoginErrorException("Expected page title `UONET+`, got " + title);
}
return symbol;
}
private Document sendCertData(Document doc) throws IOException, VulcanException {
String url = doc.select("form[name=hiddenform]").attr("action");
return client.postPageByUrl(url.replaceFirst("Default", "{symbol}"), new String[][]{
{"wa", "wsignin1.0"},
{"wresult", certificate}
{"wresult", doc.select("input[name=wresult]").val()},
{"wctx", doc.select("input[name=wctx]").val()}
});
if (html.getElementsByTag("title").text().equals("Logowanie")) {
throw new AccountPermissionException();
}
if (html.getElementsByTag("title").text().equals("Przerwa techniczna")) {
throw new VulcanOfflineException();
}
if (!html.select("title").text().equals("Uonet+")) {
throw new LoginErrorException();
}
return this.symbol;
}
private String findSymbol(String symbol, String certificate) {
@ -85,13 +83,14 @@ public class Login {
}
String findSymbolInCertificate(String certificate) {
Elements els = Jsoup.parse(certificate.replaceAll(":", ""), "", Parser.xmlParser())
Elements instances = Jsoup
.parse(certificate.replaceAll(":", ""), "", Parser.xmlParser())
.select("[AttributeName=\"UserInstance\"] samlAttributeValue");
if (els.isEmpty()) {
if (instances.isEmpty()) {
return "";
}
return els.get(1).text();
return instances.get(1).text();
}
}
}

View File

@ -1,4 +1,10 @@
package io.github.wulkanowy.api.login;
public class LoginErrorException extends NotLoggedInErrorException {
import io.github.wulkanowy.api.NotLoggedInErrorException;
class LoginErrorException extends NotLoggedInErrorException {
LoginErrorException(String message) {
super(message);
}
}

View File

@ -1,4 +0,0 @@
package io.github.wulkanowy.api.login;
public class NotLoggedInErrorException extends Exception {
}

View File

@ -1,4 +0,0 @@
package io.github.wulkanowy.api.login;
public class VulcanOfflineException extends Exception {
}

View File

@ -1,4 +1,10 @@
package io.github.wulkanowy.api.messages;
class BadRequestException extends Exception {
import io.github.wulkanowy.api.VulcanException;
class BadRequestException extends VulcanException {
BadRequestException(String message) {
super(message);
}
}

View File

@ -7,7 +7,8 @@ import java.io.IOException;
import java.util.List;
import io.github.wulkanowy.api.Client;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.NotLoggedInErrorException;
import io.github.wulkanowy.api.VulcanException;
public class Messages {
@ -37,19 +38,19 @@ public class Messages {
this.client = client;
}
public List<Message> getReceived() throws IOException, NotLoggedInErrorException, BadRequestException {
public List<Message> getReceived() throws IOException, VulcanException {
return getMessages(RECEIVED_URL);
}
public List<Message> getSent() throws IOException, NotLoggedInErrorException, BadRequestException {
public List<Message> getSent() throws IOException, VulcanException {
return getMessages(SENT_URL);
}
public List<Message> getDeleted() throws IOException, NotLoggedInErrorException, BadRequestException {
public List<Message> getDeleted() throws IOException, VulcanException {
return getMessages(DELETED_URL);
}
private List<Message> getMessages(String url) throws IOException, NotLoggedInErrorException, BadRequestException {
private List<Message> getMessages(String url) throws IOException, VulcanException {
String res = client.getJsonStringByUrl(url);
List<Message> messages;
@ -58,16 +59,16 @@ public class Messages {
messages = new Gson().fromJson(res, MessagesContainer.class).data;
} catch (JsonParseException e) {
if (res.contains(ERROR_TITLE)) {
throw new BadRequestException();
throw new BadRequestException(ERROR_TITLE);
}
throw new NotLoggedInErrorException();
throw new NotLoggedInErrorException("You are probably not logged in");
}
return messages;
}
public Message getMessage(int id, int folder) throws IOException, BadRequestException, NotLoggedInErrorException {
public Message getMessage(int id, int folder) throws IOException, VulcanException {
String res = client.postJsonStringByUrl(MESSAGE_URL, new String[][]{
{"idWiadomosc", String.valueOf(id)},
{"Folder", String.valueOf(folder)}
@ -79,10 +80,10 @@ public class Messages {
message = new Gson().fromJson(res, MessageContainer.class).data;
} catch (JsonParseException e) {
if (res.contains(ERROR_TITLE)) {
throw new BadRequestException();
throw new BadRequestException(ERROR_TITLE);
}
throw new NotLoggedInErrorException();
throw new NotLoggedInErrorException("You are probably not logged in. Force login");
}
return message;

View File

@ -8,6 +8,7 @@ import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException;
public class AchievementsList {
@ -21,7 +22,7 @@ public class AchievementsList {
this.snp = snp;
}
public List<String> getAllAchievements() throws IOException {
public List<String> getAllAchievements() throws IOException, VulcanException {
Element pageFragment = snp.getSnPPageDocument(NOTES_PAGE_URL)
.select(".mainContainer > div").get(1);
Elements items = pageFragment.select("article");

View File

@ -8,6 +8,7 @@ import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException;
public class NotesList {
@ -21,7 +22,7 @@ public class NotesList {
this.snp = snp;
}
public List<Note> getAllNotes() throws IOException {
public List<Note> getAllNotes() throws IOException, VulcanException {
Element pageFragment = snp.getSnPPageDocument(NOTES_PAGE_URL)
.select(".mainContainer > div").get(0);
Elements items = pageFragment.select("article");

View File

@ -5,6 +5,7 @@ import org.jsoup.nodes.Element;
import java.io.IOException;
import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException;
public class SchoolInfo {
@ -16,7 +17,7 @@ public class SchoolInfo {
this.snp = snp;
}
public SchoolData getSchoolData() throws IOException {
public SchoolData getSchoolData() throws IOException, VulcanException {
Element e = snp.getSnPPageDocument(SCHOOL_PAGE_URL)
.select(".mainContainer > article").get(0);

View File

@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException;
public class TeachersInfo {
@ -20,7 +21,7 @@ public class TeachersInfo {
this.snp = snp;
}
public TeachersData getTeachersData() throws IOException {
public TeachersData getTeachersData() throws IOException, VulcanException {
Document doc = snp.getSnPPageDocument(SCHOOL_PAGE_URL);
Elements rows = doc.select(".mainContainer > table tbody tr");
String description = doc.select(".mainContainer > p").first().text();

View File

@ -13,6 +13,7 @@ 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.Day;
import io.github.wulkanowy.api.generic.Lesson;
import io.github.wulkanowy.api.generic.Week;
@ -27,11 +28,11 @@ public class Timetable {
this.snp = snp;
}
public Week<Day> getWeekTable() throws IOException, ParseException {
public Week<Day> getWeekTable() throws IOException, ParseException, VulcanException {
return getWeekTable("");
}
public Week<Day> getWeekTable(final String tick) throws IOException, ParseException {
public Week<Day> getWeekTable(final String tick) throws IOException, ParseException, VulcanException {
Element table = snp.getSnPPageDocument(TIMETABLE_PAGE_URL + tick)
.select(".mainContainer .presentData").first();

View File

@ -6,6 +6,7 @@ import org.jsoup.nodes.Element;
import java.io.IOException;
import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException;
public class BasicInformation {
@ -21,7 +22,7 @@ public class BasicInformation {
this.snp = snp;
}
public Document getStudentDataPageDocument() throws IOException {
public Document getStudentDataPageDocument() throws IOException, VulcanException {
if (null == studentDataPageDocument) {
studentDataPageDocument = snp.getSnPPageDocument(STUDENT_DATA_PAGE_URL);
}
@ -29,7 +30,7 @@ public class BasicInformation {
return studentDataPageDocument;
}
public PersonalData getPersonalData() throws IOException {
public PersonalData getPersonalData() throws IOException, VulcanException {
Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(0);
String name = snp.getRowDataChildValue(e, 1);
@ -48,7 +49,7 @@ public class BasicInformation {
.setParentsNames(snp.getRowDataChildValue(e, 7));
}
public AddressData getAddressData() throws IOException {
public AddressData getAddressData() throws IOException, VulcanException {
Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(1);
return new AddressData()
@ -58,7 +59,7 @@ public class BasicInformation {
}
public ContactDetails getContactDetails() throws IOException {
public ContactDetails getContactDetails() throws IOException, VulcanException {
Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(2);
return new ContactDetails()

View File

@ -8,6 +8,7 @@ import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.SnP;
import io.github.wulkanowy.api.VulcanException;
public class FamilyInformation {
@ -19,7 +20,7 @@ public class FamilyInformation {
this.snp = snp;
}
public List<FamilyMember> getFamilyMembers() throws IOException {
public List<FamilyMember> getFamilyMembers() throws IOException, VulcanException {
Elements membersElements = snp.getSnPPageDocument(STUDENT_DATA_PAGE_URL)
.select(".mainContainer > article:nth-of-type(n+4)");

View File

@ -0,0 +1,63 @@
package io.github.wulkanowy.api;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.junit.Assert;
import org.junit.Test;
public class ClientTest {
private String getFixtureAsString(String fixtureFileName) {
return FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName));
}
@Test
public void setFullEndpointInfoTest() throws Exception {
Client client = new Client("http://fakelog.net\\\\admin", "pass", "Default");
Assert.assertEquals("fakelog.net", client.getHost());
Assert.assertEquals("Default", client.getSymbol());
}
@Test
public void checkForNoErrorsTest() throws Exception {
Client client = new Client("", "", "");
Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-success.html"));
Assert.assertEquals(doc, client.checkForErrors(doc));
}
@Test(expected = VulcanOfflineException.class)
public void checkForErrorsOffline() throws Exception {
Client client = new Client("", "", "");
Document doc = Jsoup.parse(getFixtureAsString("login/PrzerwaTechniczna.html"));
client.checkForErrors(doc);
}
@Test(expected = NotLoggedInErrorException.class)
public void checkForErrors() throws Exception {
Client client = new Client("", "", "");
Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-notLoggedIn.html"));
client.checkForErrors(doc);
}
@Test
public void getFilledUrlTest() throws Exception {
Client client = new Client("http://fakelog.cf\\\\admin", "", "symbol123");
Assert.assertEquals("http://uonetplus.fakelog.cf/symbol123/LoginEndpoint.aspx",
client.getFilledUrl("{schema}://uonetplus.{host}/{symbol}/LoginEndpoint.aspx"));
}
@Test
public void getSymbolTest() throws Exception {
Client client = new Client("", "", "symbol4321");
Assert.assertEquals("symbol4321", client.getSymbol());
}
}

View File

@ -7,11 +7,10 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
public class StudentAndParentTest {
private Client client;
@ -24,18 +23,21 @@ public class StudentAndParentTest {
client = Mockito.mock(Client.class);
Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(gradesPageDocument);
Mockito.when(client.getPageByUrl(
Mockito.anyString(),
Mockito.anyBoolean(), Mockito.anyMap())).thenReturn(gradesPageDocument);
}
@Test
public void snpTest() throws Exception {
StudentAndParent snp = new StudentAndParent(client, "id123");
Assert.assertEquals("id123", snp.getId());
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")).getSnpHomePageUrl());
(new StudentAndParent(client, "123456", null, null)).getSnpHomePageUrl());
}
@Test
@ -45,7 +47,7 @@ public class StudentAndParentTest {
Mockito.when(client.getHost()).thenReturn("vulcan.net.pl");
Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(startPageDocument);
StudentAndParent snp = new StudentAndParent(client);
StudentAndParent snp = new StudentAndParent(client, null, null, null);
Assert.assertEquals("https://uonetplus-opiekun.vulcan.net.pl/symbol/534213/Start/Index/",
snp.getSnpHomePageUrl());
@ -58,7 +60,7 @@ public class StudentAndParentTest {
);
Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(wrongPageDocument);
StudentAndParent snp = new StudentAndParent(client);
StudentAndParent snp = new StudentAndParent(client, null, null, null);
snp.getSnpHomePageUrl();
}
@ -66,7 +68,7 @@ public class StudentAndParentTest {
@Test
public void getExtractedIDStandardTest() throws Exception {
Mockito.when(client.getHost()).thenReturn("vulcan.net.pl");
StudentAndParent snp = new StudentAndParent(client, "symbol");
StudentAndParent snp = new StudentAndParent(client, "symbol", null, null);
Assert.assertEquals("123456", snp.getExtractedIdFromUrl("https://uonetplus-opiekun"
+ ".vulcan.net.pl/powiat/123456/Start/Index/"));
}
@ -74,7 +76,7 @@ public class StudentAndParentTest {
@Test
public void getExtractedIDDemoTest() throws Exception {
Mockito.when(client.getHost()).thenReturn("vulcan.net.pl");
StudentAndParent snp = new StudentAndParent(client, "symbol");
StudentAndParent snp = new StudentAndParent(client, "symbol", null, null);
Assert.assertEquals("demo12345",
snp.getExtractedIdFromUrl("https://uonetplus-opiekun.vulcan.net.pl/demoupowiat/demo12345/Start/Index/"));
}
@ -82,44 +84,75 @@ public class StudentAndParentTest {
@Test(expected = NotLoggedInErrorException.class)
public void getExtractedIDNotLoggedTest() throws Exception {
Mockito.when(client.getHost()).thenReturn("vulcan.net.pl");
StudentAndParent snp = new StudentAndParent(client, "symbol");
StudentAndParent snp = new StudentAndParent(client, "symbol", null, null);
Assert.assertEquals("123",
snp.getExtractedIdFromUrl("https://uonetplus.vulcan.net.pl/powiat/"));
}
@Test
public void getSemestersTest() throws Exception {
SnP snp = new StudentAndParent(client, "123456");
SnP snp = new StudentAndParent(client, "123456", null, null);
List<Semester> semesters = snp.getSemesters();
Assert.assertEquals(2, semesters.size());
Assert.assertEquals("1", semesters.get(0).getId());
Assert.assertEquals("1234", semesters.get(0).getNumber());
Assert.assertEquals("1234", semesters.get(0).getName());
Assert.assertFalse(semesters.get(0).isCurrent());
Assert.assertEquals("2", semesters.get(1).getId());
Assert.assertEquals("1235", semesters.get(1).getNumber());
Assert.assertEquals("1235", semesters.get(1).getName());
Assert.assertTrue(semesters.get(1).isCurrent());
}
@Test
public void getCurrentSemesterTest() throws Exception {
public void getCurrentSemesterTest() {
List<Semester> semesters = new ArrayList<>();
semesters.add(new Semester().setNumber("1500100900").setId("1").setCurrent(false));
semesters.add(new Semester().setNumber("1500100901").setId("2").setCurrent(true));
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, "");
Assert.assertTrue(snp.getCurrentSemester(semesters).isCurrent());
Assert.assertEquals("2", snp.getCurrentSemester(semesters).getId());
Assert.assertEquals("1500100901", snp.getCurrentSemester(semesters).getNumber());
SnP snp = new StudentAndParent(client, "", null, null);
Semester semester = snp.getCurrent(semesters);
Assert.assertTrue(semester.isCurrent());
Assert.assertEquals("2", semester.getId());
Assert.assertEquals("1500100901", semester.getName());
}
@Test
public void getCurrentSemesterFromEmptyTest() throws Exception {
SnP snp = new StudentAndParent(client, "");
public void getCurrentSemesterFromEmptyTest() {
SnP snp = new StudentAndParent(client, "", null, null);
List<Semester> semesters = new ArrayList<>();
Assert.assertNull(snp.getCurrentSemester(semesters));
Assert.assertNull(snp.getCurrent(semesters));
}
@Test
public void getDiariesAndStudentTest() throws IOException, VulcanException {
Document snpHome = Jsoup.parse(FixtureHelper.getAsString(
getClass().getResourceAsStream("StudentAndParent.html")));
client = Mockito.mock(Client.class);
Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(snpHome);
SnP snp = new StudentAndParent(client, "", null, null);
snp.setUp();
Assert.assertEquals("3Ti 2017", snp.getDiaries().get(0).getName());
Assert.assertEquals("2Ti 2016", snp.getDiaries().get(1).getName());
Assert.assertEquals("1Ti 2015", snp.getDiaries().get(2).getName());
Assert.assertEquals("1300", snp.getDiaries().get(0).getId());
Assert.assertEquals("1200", snp.getDiaries().get(1).getId());
Assert.assertEquals("1100", snp.getDiaries().get(2).getId());
Assert.assertTrue(snp.getDiaries().get(0).isCurrent());
Assert.assertFalse(snp.getDiaries().get(1).isCurrent());
Assert.assertFalse(snp.getDiaries().get(2).isCurrent());
Assert.assertEquals("100", snp.getDiaries().get(0).getStudentId());
Assert.assertEquals("Jan Kowal", snp.getStudents().get(0).getName());
Assert.assertEquals("100", snp.getStudents().get(0).getStudentId());
}
}

View File

@ -16,7 +16,7 @@ public abstract class StudentAndParentTestCase {
Mockito.when(snp.getSnPPageDocument(Mockito.anyString()))
.thenReturn(tablePageDocument);
Mockito.when(snp.getSemesters(Mockito.any(Document.class))).thenCallRealMethod();
Mockito.when(snp.getCurrentSemester(Mockito.<Semester>anyList()))
Mockito.when(snp.getCurrent(Mockito.<Semester>anyList()))
.thenCallRealMethod();
Mockito.when(snp.getRowDataChildValue(Mockito.any(Element.class),
Mockito.anyInt())).thenCallRealMethod();

View File

@ -1,86 +1,31 @@
package io.github.wulkanowy.api;
import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import java.util.HashMap;
import java.util.Map;
import io.github.wulkanowy.api.login.Login;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
public class VulcanTest {
private Vulcan vulcan;
@Test(expected = NotLoggedInErrorException.class)
public void getClientWithoutLoginTest() throws Exception {
Vulcan vulcan = new Vulcan();
@Before
public void setUp() throws Exception {
vulcan = new Vulcan();
vulcan.setClient(Mockito.mock(Client.class));
vulcan.setLogin(Mockito.mock(Login.class));
vulcan.getClient();
}
@Test
public void setFullEndpointInfoTest() throws Exception {
vulcan.login("http://fakelog.net\\\\admin", "pass", "Default", "123");
public void getClientTest() throws Exception {
Vulcan vulcan = new Vulcan();
vulcan.setCredentials("email", "password", "symbol", null, null, null);
Assert.assertEquals("http", vulcan.getProtocolSchema());
Assert.assertEquals("fakelog.net", vulcan.getLogHost());
Assert.assertEquals("admin", vulcan.getEmail());
Assert.assertThat(vulcan.getClient(), CoreMatchers.instanceOf(Client.class));
}
@Test
public void getClientTwiceTest() throws Exception {
Vulcan vulcan = new Vulcan();
Assert.assertTrue(vulcan.getClient().equals(vulcan.getClient()));
}
vulcan.setCredentials("email", "password", "symbol", null, null, null);
@Test
public void getLoginTwiceTest() throws Exception {
Vulcan vulcan = new Vulcan();
Assert.assertTrue(vulcan.getLogin().equals(vulcan.getLogin()));
}
@Test(expected = NotLoggedInErrorException.class)
public void getStudentAndParentNotLoggedInTest() throws Exception {
vulcan.getStudentAndParent();
}
@Test
public void getStudentAndParentTwiceTest() throws Exception {
Client client = Mockito.mock(Client.class);
Map<String, String> cookies = new HashMap<>();
cookies.put("test", "test");
Mockito.when(client.getCookies()).thenReturn(cookies);
SnP snp = Mockito.mock(StudentAndParent.class);
Mockito.doNothing().when(snp).storeContextCookies();
Vulcan vulcan = Mockito.mock(Vulcan.class);
Mockito.when(vulcan.getClient()).thenReturn(client);
Mockito.when(vulcan.getStudentAndParent()).thenCallRealMethod();
Mockito.when(vulcan.createSnp(Mockito.any(Client.class), Mockito.any())).thenReturn(snp);
vulcan.getStudentAndParent();
vulcan.getStudentAndParent();
}
@Test
public void createSnPTest() throws Exception {
vulcan.login("wulkanowy@wulkanowy.io", "wulkanowy123", "wulkan");
SnP snp1 = vulcan.createSnp(Mockito.mock(Client.class), null);
Assert.assertEquals(null, snp1.getId());
SnP snp2 = vulcan.createSnp(Mockito.mock(Client.class), "wulkan");
Assert.assertEquals("wulkan", snp2.getId());
}
@Test(expected = NotLoggedInErrorException.class)
public void getAttendanceExceptionText() throws Exception {
vulcan.getAttendanceTable();
Assert.assertEquals(vulcan.getClient(), vulcan.getClient());
}
}

View File

@ -20,8 +20,8 @@ public class AttendanceTableTest extends StudentAndParentTestCase {
@Test
public void getWeekStartByDate() throws Exception {
Assert.assertEquals("31.08.2015", excellent.getWeekTable().getStartDayDate());
Assert.assertEquals("05.09.2016", full.getWeekTable().getStartDayDate());
Assert.assertEquals("2015-08-31", excellent.getWeekTable().getStartDayDate());
Assert.assertEquals("2016-09-05", full.getWeekTable().getStartDayDate());
}
@Test
@ -38,13 +38,13 @@ public class AttendanceTableTest extends StudentAndParentTestCase {
@Test
public void getDayDate() throws Exception {
Assert.assertEquals("31.08.2015", excellent.getWeekTable().getDay(0).getDate());
Assert.assertEquals("02.09.2015", excellent.getWeekTable().getDay(2).getDate());
Assert.assertEquals("04.09.2015", excellent.getWeekTable().getDay(4).getDate());
Assert.assertEquals("2015-08-31", excellent.getWeekTable().getDay(0).getDate());
Assert.assertEquals("2015-09-02", excellent.getWeekTable().getDay(2).getDate());
Assert.assertEquals("2015-09-04", excellent.getWeekTable().getDay(4).getDate());
Assert.assertEquals("05.09.2016", full.getWeekTable().getDay(0).getDate());
Assert.assertEquals("07.09.2016", full.getWeekTable().getDay(2).getDate());
Assert.assertEquals("09.09.2016", full.getWeekTable().getDay(4).getDate());
Assert.assertEquals("2016-09-05", full.getWeekTable().getDay(0).getDate());
Assert.assertEquals("2016-09-07", full.getWeekTable().getDay(2).getDate());
Assert.assertEquals("2016-09-09", full.getWeekTable().getDay(4).getDate());
}
@Test

View File

@ -1,6 +1,5 @@
package io.github.wulkanowy.api.login;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.junit.Assert;
@ -12,12 +11,16 @@ import io.github.wulkanowy.api.FixtureHelper;
public class LoginTest {
private Document getFixtureAsDocument(String fixtureFileName) {
return Jsoup.parse(getFixtureAsString(fixtureFileName));
}
private String getFixtureAsString(String fixtureFileName) {
return FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName));
}
private Client getClient(String fixtureFileName) throws Exception {
Document doc = Jsoup.parse(getFixtureAsString(fixtureFileName));
Document doc = getFixtureAsDocument(fixtureFileName);
Client client = Mockito.mock(Client.class);
Mockito.when(client.postPageByUrl(Mockito.anyString(), Mockito.any(String[][].class))).thenReturn(doc);
@ -27,25 +30,36 @@ public class LoginTest {
@Test
public void loginTest() throws Exception {
Login login = new Login(getClient("Logowanie-success.html"));
Client client = getClient("Logowanie-success.html");
Mockito.when(client.getPageByUrl(Mockito.anyString(), Mockito.anyBoolean()))
.thenReturn(getFixtureAsDocument("Logowanie-error.html"));
Login login = new Login(client);
Assert.assertEquals("d123", login.login("a@a", "pswd", "d123"));
}
@Test(expected = BadCredentialsException.class)
public void sendWrongCredentialsTest() throws Exception {
Login login = new Login(getClient("Logowanie-error.html"));
Client client = getClient("Logowanie-error.html");
Mockito.when(client.getPageByUrl(Mockito.anyString(), Mockito.anyBoolean()))
.thenReturn(getFixtureAsDocument("Logowanie-error.html")); // -error.html because it html with form used by
Login login = new Login(client);
login.sendCredentials("a@a", "pswd", "d123");
login.sendCredentials("a@a", "pswd");
}
@Test
public void sendCredentialsCertificateTest() throws Exception {
Login login = new Login(getClient("Logowanie-certyfikat.html"));
Client client = getClient("Logowanie-certyfikat.html");
Mockito.when(client.getPageByUrl(Mockito.anyString(), Mockito.anyBoolean()))
.thenReturn(getFixtureAsDocument("Logowanie-error.html")); // -error.html because it html with form used by
Login login = new Login(client);
Assert.assertEquals(
getFixtureAsString("cert.xml").replaceAll("\\s+",""),
login.sendCredentials("a@a", "passwd", "d123").replaceAll("\\s+","")
login.sendCredentials("a@a", "passwd")
.select("input[name=wresult]").attr("value")
.replaceAll("\\s+","")
);
}
@ -54,7 +68,7 @@ public class LoginTest {
Login login = new Login(getClient("Logowanie-success.html"));
Assert.assertEquals("wulkanowyschool321",
login.sendCertificate("", "wulkanowyschool321"));
login.sendCertificate(new Document(""), "wulkanowyschool321"));
}
@Test
@ -62,28 +76,21 @@ public class LoginTest {
Login login = new Login(getClient("Logowanie-success.html"));
Assert.assertEquals("demo12345",
login.sendCertificate(getFixtureAsString("cert.xml"), "Default"));
login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "Default"));
}
@Test(expected = AccountPermissionException.class)
public void sendCertificateAccountPermissionTest() throws Exception {
Login login = new Login(getClient("Logowanie-brak-dostepu.html"));
login.sendCertificate(getFixtureAsString("cert.xml"), "demo123");
login.sendCertificate(getFixtureAsDocument("cert.xml"), "demo123");
}
@Test(expected = LoginErrorException.class)
public void sendCertificateLoginErrorTest() throws Exception {
Login login = new Login(getClient("Logowanie-certyfikat.html")); // change to other document
login.sendCertificate(getFixtureAsString("cert.xml"), "demo123");
}
@Test(expected = VulcanOfflineException.class)
public void sendCertificateVulcanOfflineTest() throws Exception {
Login login = new Login(getClient("PrzerwaTechniczna.html"));
login.sendCertificate(getFixtureAsString("cert.xml"), "demo123");
login.sendCertificate(getFixtureAsDocument("cert.xml"), "demo123");
}
@Test

View File

@ -8,7 +8,7 @@ import java.util.List;
import io.github.wulkanowy.api.Client;
import io.github.wulkanowy.api.FixtureHelper;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.NotLoggedInErrorException;
public class MessagesTest {

View File

@ -15,7 +15,8 @@
<option selected="selected" value="1235">2</option>
</select>
</div>
</main>
</div>
</main>
<footer>wersja: 17.05.0000.24042</footer>
</body>
</html>

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="utf-8">
<title>Witryna ucznia i rodzica Strona gł&#243;wna</title>
</head>
<body>
<ul id="idSection">
<li>
<label for="uczenDropDownList">Uczeń:</label>
<select id="uczenDropDownList" name="uczenDropDownList">
<option selected="selected"
value="https://uonetplus-opiekun.fakelog.cf/Default/123456/Uczen/UczenOnChange?&amp;id=100">
Jan Kowal
</option>
</select>
</li>
<li>
<label for="dziennikDropDownList">Dziennik:</label>
<select id="dziennikDropDownList" name="dziennikDropDownList">
<option selected="selected"
value="https://uonetplus-opiekun.fakelog.cf/Default/123456/Dziennik/DziennikOnChange?&amp;id=1300">
3Ti 2017
</option>
<option value="https://uonetplus-opiekun.fakelog.cf/Default/123456/Dziennik/DziennikOnChange?&amp;id=1200">
2Ti 2016
</option>
<option value="https://uonetplus-opiekun.fakelog.cf/Default/123456/Dziennik/DziennikOnChange?&amp;id=1100">
1Ti 2015
</option>
</select>
</li>
</ul>
<footer>wersja: 17.09.0008.26553</footer>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Dziennik UONET+</title>
</head>
<body>
<div id="MainPage_InfoPage">
<div class="startScreen">
<div class="topBar">
<div class="loginBox">
<div>
<a href="/LoginEndpoint.aspx" class="loginButton"></a>
<a href="/LoginEndpoint.aspx" class="loginButton">Zaloguj się</a>
</div>
</div>
</div>
<div class="bottomBar"><span>Uonet+ wersja 17.09.0007.26300</span></div>
</div>
</div>
</body>
</html>

View File

@ -21,15 +21,15 @@ apply from: '../jacoco.gradle'
apply from: '../android-sonarqube.gradle'
android {
compileSdkVersion 27
buildToolsVersion "27.0.3"
compileSdkVersion 26
buildToolsVersion '27.0.3'
defaultConfig {
applicationId "io.github.wulkanowy"
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 15
targetSdkVersion 27
versionCode 1
versionName "0.1.0"
targetSdkVersion 26
versionCode 7
versionName "0.3.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
manifestPlaceholders = [
@ -43,6 +43,8 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
testCoverageEnabled = true
ext.enableCrashlytics = false
}
@ -60,47 +62,45 @@ android {
}
greendao {
schemaVersion 20
schemaVersion 23
generateTests = true
}
dependencies {
implementation project(':api')
implementation 'com.android.support:appcompat-v7:27.1.0'
implementation 'com.android.support:design:27.1.0'
implementation 'com.android.support:support-v4:27.1.0'
implementation 'com.android.support:recyclerview-v7:27.1.0'
implementation 'com.android.support:cardview-v7:27.1.0'
implementation 'com.android.support:customtabs:27.1.0'
implementation 'com.firebase:firebase-jobdispatcher:0.8.5'
implementation 'org.apache.commons:commons-lang3:3.7'
implementation 'eu.davidea:flexible-adapter:5.0.0-rc4'
implementation 'eu.davidea:flexible-adapter-ui:1.0.0-b1'
implementation 'org.apache.commons:commons-collections4:4.1'
implementation 'org.greenrobot:greendao:3.2.2'
implementation 'com.jakewharton:butterknife:8.8.1'
implementation 'joda-time:joda-time:2.9.9'
implementation 'com.google.dagger:dagger-android:2.14.1'
implementation 'com.google.dagger:dagger-android-support:2.14.1'
implementation 'com.aurelhubert:ahbottomnavigation:2.1.0'
implementation "com.android.support:support-v4:$supportVersion"
implementation "com.android.support:design:$supportVersion"
implementation "com.android.support:cardview-v7:$supportVersion"
implementation "com.android.support:customtabs:$supportVersion"
implementation "com.android.support:preference-v14:$supportVersion"
implementation "com.firebase:firebase-jobdispatcher:$firebaseJob"
implementation "org.apache.commons:commons-lang3:$apacheLang"
implementation "org.apache.commons:commons-collections4:$apacheCollections"
implementation "eu.davidea:flexible-adapter:$flexibleAdapter"
implementation "eu.davidea:flexible-adapter-ui:$flexibleUi"
implementation "org.greenrobot:greendao:$greenDao"
implementation "com.jakewharton:butterknife:$butterknife"
implementation "com.google.dagger:dagger-android-support:$dagger2"
implementation "com.aurelhubert:ahbottomnavigation:$ahbottom"
implementation "com.jakewharton.threetenabp:threetenabp:$threeTenABP"
implementation('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {
implementation("com.crashlytics.sdk.android:crashlytics:$crashlyticsSdk@aar") {
transitive = true
}
implementation('com.crashlytics.sdk.android:answers:1.4.1@aar') {
implementation("com.crashlytics.sdk.android:answers:$crashlyticsAnswers@aar") {
transitive = true
}
annotationProcessor 'com.google.dagger:dagger-android-processor:2.14.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger2"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger2"
annotationProcessor "com.jakewharton:butterknife-compiler:$butterknife"
debugImplementation 'com.amitshekhar.android:debug-db:1.0.1'
debugImplementation 'net.zetetic:android-database-sqlcipher:3.5.9'
debugImplementation "com.amitshekhar.android:debug-db:$debugDb"
debugImplementation "net.zetetic:android-database-sqlcipher:$sqlcipher"
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.13.0'
testImplementation "junit:junit:$junit"
testImplementation "org.mockito:mockito-core:$mockito"
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'org.mockito:mockito-android:2.13.0'
androidTestImplementation "com.android.support.test:runner:$testRunner"
androidTestImplementation "org.mockito:mockito-android:$mockito"
}

View File

@ -0,0 +1,25 @@
package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.test.AbstractDaoTestLongPk;
public class AttendanceLessonTest extends AbstractDaoTestLongPk<AttendanceLessonDao, AttendanceLesson> {
public AttendanceLessonTest() {
super(AttendanceLessonDao.class);
}
@Override
protected AttendanceLesson createEntity(Long key) {
AttendanceLesson entity = new AttendanceLesson();
entity.setId(key);
entity.setIsPresence(false);
entity.setIsAbsenceUnexcused(false);
entity.setIsAbsenceExcused(false);
entity.setIsUnexcusedLateness(false);
entity.setIsAbsenceForSchoolReasons(false);
entity.setIsExcusedLateness(false);
entity.setIsExemption(false);
return entity;
}
}

View File

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

View File

@ -2,15 +2,15 @@ package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.test.AbstractDaoTestLongPk;
public class LessonTest extends AbstractDaoTestLongPk<LessonDao, Lesson> {
public class TimetableLessonTest extends AbstractDaoTestLongPk<TimetableLessonDao, TimetableLesson> {
public LessonTest() {
super(LessonDao.class);
public TimetableLessonTest() {
super(TimetableLessonDao.class);
}
@Override
protected Lesson createEntity(Long key) {
Lesson entity = new Lesson();
protected TimetableLesson createEntity(Long key) {
TimetableLesson entity = new TimetableLesson();
entity.setId(key);
entity.setIsEmpty(false);
entity.setIsDivisionIntoGroups(false);

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">Wulkanowy DEV</string>
</resources>

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">Wulkanowy DEV</string>
</resources>

View File

@ -4,6 +4,7 @@ import android.app.Application;
import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.core.CrashlyticsCore;
import com.jakewharton.threetenabp.AndroidThreeTen;
import org.greenrobot.greendao.query.QueryBuilder;
@ -16,6 +17,7 @@ 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;
public class WulkanowyApp extends Application {
@ -27,6 +29,8 @@ public class WulkanowyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
AndroidThreeTen.init(this);
applicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(this))
@ -37,6 +41,17 @@ public class WulkanowyApp extends Application {
enableDebugLog();
}
initializeFabric();
initializeUserSession();
}
private void initializeUserSession() {
if (repository.getCurrentUserId() != 0) {
try {
repository.initLastUser();
} catch (Exception e) {
LogUtils.error("An error occurred when the application was started", e);
}
}
}
private void enableDebugLog() {

View File

@ -1,6 +1,5 @@
package io.github.wulkanowy.data;
import java.io.IOException;
import java.text.ParseException;
import java.util.List;
@ -8,11 +7,9 @@ import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.api.login.AccountPermissionException;
import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.GradeDao;
@ -21,7 +18,8 @@ import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.data.db.resources.ResourcesContract;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.data.sync.SyncContract;
import io.github.wulkanowy.data.sync.login.LoginSyncContract;
import io.github.wulkanowy.data.sync.account.AccountSyncContract;
import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract;
import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract;
import io.github.wulkanowy.di.annotations.SyncGrades;
import io.github.wulkanowy.di.annotations.SyncSubjects;
@ -36,7 +34,9 @@ public class Repository implements RepositoryContract {
private final DaoSession daoSession;
private final LoginSyncContract loginSync;
private final AccountSyncContract accountSync;
private final AttendanceSyncContract attendanceSync;
private final TimetableSyncContract timetableSync;
@ -48,14 +48,16 @@ public class Repository implements RepositoryContract {
Repository(SharedPrefContract sharedPref,
ResourcesContract resources,
DaoSession daoSession,
LoginSyncContract loginSync,
AccountSyncContract accountSync,
AttendanceSyncContract attendanceSync,
TimetableSyncContract timetableSync,
@SyncGrades SyncContract gradeSync,
@SyncSubjects SyncContract subjectSync) {
this.sharedPref = sharedPref;
this.resources = resources;
this.daoSession = daoSession;
this.loginSync = loginSync;
this.accountSync = accountSync;
this.attendanceSync = attendanceSync;
this.timetableSync = timetableSync;
this.gradeSync = gradeSync;
this.subjectSync = subjectSync;
@ -66,6 +68,31 @@ public class Repository implements RepositoryContract {
return sharedPref.getCurrentUserId();
}
@Override
public int getStartupTab() {
return sharedPref.getStartupTab();
}
@Override
public int getServicesInterval() {
return sharedPref.getServicesInterval();
}
@Override
public boolean isServicesEnable() {
return sharedPref.isServicesEnable();
}
@Override
public boolean isNotifyEnable() {
return sharedPref.isNotifyEnable();
}
@Override
public boolean isMobileDisable() {
return sharedPref.isMobileDisable();
}
@Override
public String[] getSymbolsKeysArray() {
return resources.getSymbolsKeysArray();
@ -82,42 +109,56 @@ public class Repository implements RepositoryContract {
}
@Override
public void loginUser(String email, String password, String symbol)
throws NotLoggedInErrorException, AccountPermissionException, IOException,
CryptoException, VulcanOfflineException, BadCredentialsException {
loginSync.loginUser(email, password, symbol);
public String getAttendanceLessonDescription(AttendanceLesson lesson) {
return resources.getAttendanceLessonDescription(lesson);
}
@Override
public void loginCurrentUser() throws NotLoggedInErrorException, AccountPermissionException,
IOException, CryptoException, VulcanOfflineException, BadCredentialsException {
loginSync.loginCurrentUser();
public void registerUser(String email, String password, String symbol) throws VulcanException,
IOException, CryptoException {
accountSync.registerUser(email, password, symbol);
}
@Override
public void syncGrades() throws NotLoggedInErrorException, IOException, ParseException {
public void initLastUser() throws VulcanException, IOException, CryptoException {
accountSync.initLastUser();
}
@Override
public void syncGrades() throws VulcanException, IOException, ParseException {
gradeSync.sync();
}
@Override
public void syncSubjects() throws NotLoggedInErrorException, IOException, ParseException {
public void syncSubjects() throws VulcanException, IOException, ParseException {
subjectSync.sync();
}
@Override
public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException {
public void syncAttendance() throws ParseException, IOException, VulcanException {
attendanceSync.syncAttendance();
}
@Override
public void syncAttendance(String date) throws ParseException, IOException, VulcanException {
attendanceSync.syncAttendance(date);
}
@Override
public void syncTimetable() throws VulcanException, IOException, ParseException {
timetableSync.syncTimetable();
}
@Override
public void syncTimetable(String date) throws NotLoggedInErrorException, IOException, ParseException {
public void syncTimetable(String date) throws VulcanException, IOException, ParseException {
timetableSync.syncTimetable(date);
}
@Override
public void syncAll() throws NotLoggedInErrorException, IOException, ParseException {
public void syncAll() throws VulcanException, IOException, ParseException {
syncSubjects();
syncGrades();
syncAttendance();
syncTimetable();
}

View File

@ -6,25 +6,36 @@ import java.util.List;
import javax.inject.Singleton;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.resources.ResourcesContract;
import io.github.wulkanowy.data.sync.login.LoginSyncContract;
import io.github.wulkanowy.data.sync.account.AccountSyncContract;
import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract;
import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract;
@Singleton
public interface RepositoryContract extends ResourcesContract, LoginSyncContract,
TimetableSyncContract {
public interface RepositoryContract extends ResourcesContract, AccountSyncContract,
AttendanceSyncContract, TimetableSyncContract {
long getCurrentUserId();
void syncGrades() throws NotLoggedInErrorException, IOException, ParseException;
int getStartupTab();
void syncSubjects() throws NotLoggedInErrorException, IOException, ParseException;
boolean isServicesEnable();
void syncAll() throws NotLoggedInErrorException, IOException, ParseException;
boolean isNotifyEnable();
int getServicesInterval();
boolean isMobileDisable();
void syncGrades() throws VulcanException, IOException, ParseException;
void syncSubjects() throws VulcanException, IOException, ParseException;
void syncAll() throws VulcanException, IOException, ParseException;
Account getCurrentUser();

View File

@ -1,19 +1,91 @@
package io.github.wulkanowy.data.db.dao;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.StandardDatabase;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.inject.Inject;
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.shared.SharedPrefContract;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.di.annotations.DatabaseInfo;
import io.github.wulkanowy.utils.LogUtils;
@Singleton
public class DbHelper extends DaoMaster.DevOpenHelper {
public class DbHelper extends DaoMaster.OpenHelper {
private final SharedPrefContract sharedPref;
private final Vulcan vulcan;
@Inject
DbHelper(@ApplicationContext Context context, @DatabaseInfo String dbName) {
DbHelper(@ApplicationContext Context context, @DatabaseInfo String dbName,
SharedPrefContract sharedPref, Vulcan vulcan) {
super(context, dbName);
this.sharedPref = sharedPref;
this.vulcan = vulcan;
}
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Database database = new StandardDatabase(db);
DaoMaster.dropAllTables(database, true);
onCreate(database);
sharedPref.setCurrentUserId(0);
LogUtils.info("Cleaning user data oldVersion=" + oldVersion + " newVersion=" + newVersion);
}
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
List<Migration> migrations = getMigrations();
// Only run migrations past the old version
for (Migration migration : migrations) {
if (oldVersion < migration.getVersion()) {
try {
LogUtils.info("Applying migration to db schema v" + migration.getVersion() + "...");
migration.runMigration(db, sharedPref, vulcan);
LogUtils.info("Migration " + migration.getVersion() + " complete");
} catch (Exception e) {
e.printStackTrace();
DaoMaster.dropAllTables(db, true);
sharedPref.setCurrentUserId(0);
break;
}
}
}
}
private List<Migration> getMigrations() {
List<Migration> migrations = new ArrayList<>();
migrations.add(new Migration23());
// Sorting just to be safe, in case other people add migrations in the wrong order.
Comparator<Migration> migrationComparator = new Comparator<Migration>() {
@Override
public int compare(Migration m1, Migration m2) {
return m1.getVersion().compareTo(m2.getVersion());
}
};
Collections.sort(migrations, migrationComparator);
return migrations;
}
public interface Migration {
Integer getVersion();
void runMigration(Database db, SharedPrefContract sharedPref, Vulcan vulcan) throws Exception;
}
}

View File

@ -4,6 +4,8 @@ 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.JoinProperty;
import org.greenrobot.greendao.annotation.Property;
import org.greenrobot.greendao.annotation.ToMany;
@ -18,20 +20,29 @@ public class Account {
@Id(autoincrement = true)
private Long id;
@Index(unique = true)
@Property(nameInDb = "REAL_ID")
private String realId;
@Property(nameInDb = "SYMBOL")
private String symbol;
@Property(nameInDb = "SCHOOL_ID")
private String schoolId;
@Property(nameInDb = "NAME")
private String name;
@Property(nameInDb = "E-MAIL")
@Property(nameInDb = "E_MAIL")
private String email;
@Property(nameInDb = "PASSWORD")
private String password;
@Property(nameInDb = "SYMBOL")
private String symbol;
@Property(nameInDb = "SNPID")
private String snpId;
@ToMany(joinProperties = {
@JoinProperty(name = "realId", referencedName = "studentId")
})
private List<Diary> diaryList;
@ToMany(referencedJoinProperty = "userId")
private List<Subject> subjectList;
@ -54,15 +65,16 @@ public class Account {
@Generated(hash = 335469827)
private transient AccountDao myDao;
@Generated(hash = 735765217)
public Account(Long id, String name, String email, String password, String symbol,
String snpId) {
@Generated(hash = 727721142)
public Account(Long id, String realId, String symbol, String schoolId, String name,
String email, String password) {
this.id = id;
this.realId = realId;
this.symbol = symbol;
this.schoolId = schoolId;
this.name = name;
this.email = email;
this.password = password;
this.symbol = symbol;
this.snpId = snpId;
}
@Generated(hash = 882125521)
@ -78,6 +90,15 @@ public class Account {
return this;
}
public String getRealId() {
return realId;
}
public Account setRealId(String realId) {
this.realId = realId;
return this;
}
public String getName() {
return name;
}
@ -114,15 +135,83 @@ public class Account {
return this;
}
public String getSnpId() {
return this.snpId;
public String getSchoolId() {
return schoolId;
}
public Account setSnpId(String snpId) {
this.snpId = snpId;
public Account setSchoolId(String schoolId) {
this.schoolId = schoolId;
return this;
}
public Account setDiaryList(List<Diary> diaryList) {
this.diaryList = diaryList;
return this;
}
public Account setSubjectList(List<Subject> subjectList) {
this.subjectList = subjectList;
return this;
}
public Account setGradeList(List<Grade> gradeList) {
this.gradeList = gradeList;
return this;
}
public Account setDayList(List<Day> dayList) {
this.dayList = dayList;
return this;
}
public DaoSession getDaoSession() {
return daoSession;
}
public Account setDaoSession(DaoSession daoSession) {
this.daoSession = daoSession;
return this;
}
public AccountDao getMyDao() {
return myDao;
}
public Account setMyDao(AccountDao myDao) {
this.myDao = myDao;
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 = 1472214466)
public List<Diary> getDiaryList() {
if (diaryList == null) {
final DaoSession daoSession = this.daoSession;
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
DiaryDao targetDao = daoSession.getDiaryDao();
List<Diary> diaryListNew = targetDao._queryAccount_DiaryList(realId);
synchronized (this) {
if (diaryList == null) {
diaryList = diaryListNew;
}
}
}
return diaryList;
}
/**
* Resets a to-many relationship, making the next get call to query for a fresh result.
*/
@Generated(hash = 1078514341)
public synchronized void resetDiaryList() {
diaryList = 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.
@ -183,6 +272,36 @@ public class Account {
gradeList = 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 = 300459794)
public List<Day> getDayList() {
if (dayList == null) {
final DaoSession daoSession = this.daoSession;
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
DayDao targetDao = daoSession.getDayDao();
List<Day> dayListNew = targetDao._queryAccount_DayList(id);
synchronized (this) {
if (dayList == null) {
dayList = dayListNew;
}
}
}
return dayList;
}
/**
* Resets a to-many relationship, making the next get call to query for a fresh result.
*/
@Generated(hash = 1010399236)
public synchronized void resetDayList() {
dayList = null;
}
/**
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
* Entity must attached to an entity context.
@ -219,36 +338,6 @@ public class Account {
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 = 300459794)
public List<Day> getDayList() {
if (dayList == null) {
final DaoSession daoSession = this.daoSession;
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
DayDao targetDao = daoSession.getDayDao();
List<Day> dayListNew = targetDao._queryAccount_DayList(id);
synchronized (this) {
if (dayList == null) {
dayList = dayListNew;
}
}
}
return dayList;
}
/**
* Resets a to-many relationship, making the next get call to query for a fresh result.
*/
@Generated(hash = 1010399236)
public synchronized void resetDayList() {
dayList = null;
}
/** called by internal mechanisms, do not call yourself. */
@Generated(hash = 1812283172)
public void __setDaoSession(DaoSession daoSession) {

View File

@ -0,0 +1,254 @@
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.Index;
import org.greenrobot.greendao.annotation.Property;
import org.greenrobot.greendao.annotation.Transient;
import java.io.Serializable;
@Entity(
nameInDb = "AttendanceLessons",
active = true,
indexes = {@Index(value = "dayId,date,number", unique = true)}
)
public class AttendanceLesson implements Serializable {
@Id(autoincrement = true)
private Long id;
@Property(nameInDb = "DAY_ID")
private Long dayId;
@Property(nameInDb = "DATE")
private String date = "";
@Property(nameInDb = "NUMBER_OF_LESSON")
private int number = 0;
@Property(nameInDb = "SUBJECT_NAME")
private String subject = "";
@Property(nameInDb = "IS_PRESENCE")
private boolean isPresence = false;
@Property(nameInDb = "IS_ABSENCE_UNEXCUSED")
private boolean isAbsenceUnexcused = false;
@Property(nameInDb = "IS_ABSENCE_EXCUSED")
private boolean isAbsenceExcused = false;
@Property(nameInDb = "IS_UNEXCUSED_LATENESS")
private boolean isUnexcusedLateness = false;
@Property(nameInDb = "IS_ABSENCE_FOR_SCHOOL_REASONS")
private boolean isAbsenceForSchoolReasons = false;
@Property(nameInDb = "IS_EXCUSED_LATENESS")
private boolean isExcusedLateness = false;
@Property(nameInDb = "IS_EXEMPTION")
private boolean isExemption = false;
@Transient
private String description = "";
private static final long serialVersionUID = 42L;
/**
* Used to resolve relations
*/
@Generated(hash = 2040040024)
private transient DaoSession daoSession;
/**
* Used for active entity operations.
*/
@Generated(hash = 1936953859)
private transient AttendanceLessonDao myDao;
@Generated(hash = 1428129046)
public AttendanceLesson(Long id, Long dayId, String date, int number,
String subject, boolean isPresence, boolean isAbsenceUnexcused,
boolean isAbsenceExcused, boolean isUnexcusedLateness,
boolean isAbsenceForSchoolReasons, boolean isExcusedLateness,
boolean isExemption) {
this.id = id;
this.dayId = dayId;
this.date = date;
this.number = number;
this.subject = subject;
this.isPresence = isPresence;
this.isAbsenceUnexcused = isAbsenceUnexcused;
this.isAbsenceExcused = isAbsenceExcused;
this.isUnexcusedLateness = isUnexcusedLateness;
this.isAbsenceForSchoolReasons = isAbsenceForSchoolReasons;
this.isExcusedLateness = isExcusedLateness;
this.isExemption = isExemption;
}
@Generated(hash = 921806575)
public AttendanceLesson() {
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public Long getDayId() {
return this.dayId;
}
public void setDayId(Long dayId) {
this.dayId = dayId;
}
public String getDate() {
return this.date;
}
public AttendanceLesson setDate(String date) {
this.date = date;
return this;
}
public int getNumber() {
return this.number;
}
public AttendanceLesson setNumber(int number) {
this.number = number;
return this;
}
public String getSubject() {
return this.subject;
}
public AttendanceLesson setSubject(String subject) {
this.subject = subject;
return this;
}
public boolean getIsPresence() {
return this.isPresence;
}
public AttendanceLesson setIsPresence(boolean isPresence) {
this.isPresence = isPresence;
return this;
}
public boolean getIsAbsenceUnexcused() {
return this.isAbsenceUnexcused;
}
public AttendanceLesson setIsAbsenceUnexcused(boolean isAbsenceUnexcused) {
this.isAbsenceUnexcused = isAbsenceUnexcused;
return this;
}
public boolean getIsAbsenceExcused() {
return this.isAbsenceExcused;
}
public AttendanceLesson setIsAbsenceExcused(boolean isAbsenceExcused) {
this.isAbsenceExcused = isAbsenceExcused;
return this;
}
public boolean getIsUnexcusedLateness() {
return this.isUnexcusedLateness;
}
public AttendanceLesson setIsUnexcusedLateness(boolean isUnexcusedLateness) {
this.isUnexcusedLateness = isUnexcusedLateness;
return this;
}
public boolean getIsAbsenceForSchoolReasons() {
return this.isAbsenceForSchoolReasons;
}
public AttendanceLesson setIsAbsenceForSchoolReasons(boolean isAbsenceForSchoolReasons) {
this.isAbsenceForSchoolReasons = isAbsenceForSchoolReasons;
return this;
}
public boolean getIsExcusedLateness() {
return this.isExcusedLateness;
}
public AttendanceLesson setIsExcusedLateness(boolean isExcusedLateness) {
this.isExcusedLateness = isExcusedLateness;
return this;
}
public boolean getIsExemption() {
return this.isExemption;
}
public AttendanceLesson setIsExemption(boolean isExemption) {
this.isExemption = isExemption;
return this;
}
public String getDescription() {
return description;
}
public AttendanceLesson setDescription(String description) {
this.description = description;
return this;
}
/**
* 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 = 1157101112)
public void __setDaoSession(DaoSession daoSession) {
this.daoSession = daoSession;
myDao = daoSession != null ? daoSession.getAttendanceLessonDao() : null;
}
}

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;
@ -13,7 +14,7 @@ import java.util.List;
@Entity(
nameInDb = "Days",
active = true,
indexes ={@Index(value = "userId,weekId,date", unique = true)}
indexes = {@Index(value = "userId,weekId,date", unique = true)}
)
public class Day {
@ -39,7 +40,11 @@ public class Day {
private String freeDayName = "";
@ToMany(referencedJoinProperty = "dayId")
private List<Lesson> lessons;
private List<TimetableLesson> timetableLessons;
@ToMany(referencedJoinProperty = "dayId")
@OrderBy("number ASC")
private List<AttendanceLesson> attendanceLessons;
/**
* Used to resolve relations
@ -113,12 +118,12 @@ public class Day {
return this;
}
public boolean isFreeDay() {
return isFreeDay;
public boolean getIsFreeDay() {
return this.isFreeDay;
}
public Day setFreeDay(boolean freeDay) {
isFreeDay = freeDay;
public Day setIsFreeDay(boolean isFreeDay) {
this.isFreeDay = isFreeDay;
return this;
}
@ -131,42 +136,62 @@ public class Day {
return this;
}
public boolean getIsFreeDay() {
return this.isFreeDay;
/**
* 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 = 218588195)
public List<TimetableLesson> getTimetableLessons() {
if (timetableLessons == null) {
final DaoSession daoSession = this.daoSession;
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
TimetableLessonDao targetDao = daoSession.getTimetableLessonDao();
List<TimetableLesson> timetableLessonsNew = targetDao
._queryDay_TimetableLessons(id);
synchronized (this) {
if (timetableLessons == null) {
timetableLessons = timetableLessonsNew;
}
}
}
return timetableLessons;
}
public void setIsFreeDay(boolean isFreeDay) {
this.isFreeDay = isFreeDay;
/** Resets a to-many relationship, making the next get call to query for a fresh result. */
@Generated(hash = 1687683740)
public synchronized void resetTimetableLessons() {
timetableLessons = 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 = 1552857303)
public List<Lesson> getLessons() {
if (lessons == null) {
@Generated(hash = 1166820581)
public List<AttendanceLesson> getAttendanceLessons() {
if (attendanceLessons == null) {
final DaoSession daoSession = this.daoSession;
if (daoSession == null) {
throw new DaoException("Entity is detached from DAO context");
}
LessonDao targetDao = daoSession.getLessonDao();
List<Lesson> lessonsNew = targetDao._queryDay_Lessons(id);
AttendanceLessonDao targetDao = daoSession.getAttendanceLessonDao();
List<AttendanceLesson> attendanceLessonsNew = targetDao
._queryDay_AttendanceLessons(id);
synchronized (this) {
if (lessons == null) {
lessons = lessonsNew;
if (attendanceLessons == null) {
attendanceLessons = attendanceLessonsNew;
}
}
}
return lessons;
return attendanceLessons;
}
/**
* Resets a to-many relationship, making the next get call to query for a fresh result.
*/
@Generated(hash = 1769801440)
public synchronized void resetLessons() {
lessons = null;
/** Resets a to-many relationship, making the next get call to query for a fresh result. */
@Generated(hash = 1343075564)
public synchronized void resetAttendanceLessons() {
attendanceLessons = null;
}
/**

View File

@ -0,0 +1,161 @@
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;
@Entity(
nameInDb = "Diaries",
active = true
)
public class Diary {
@Id(autoincrement = true)
private Long id;
@Property(nameInDb = "STUDENT_ID")
private String studentId;
@Property(nameInDb = "NAME")
private String name;
@Property(nameInDb = "VALUE")
private String value;
@Property(nameInDb = "IS_CURRENT")
private boolean isCurrent;
/**
* Used to resolve relations
*/
@Generated(hash = 2040040024)
private transient DaoSession daoSession;
/**
* Used for active entity operations.
*/
@Generated(hash = 21166549)
private transient DiaryDao myDao;
@Generated(hash = 459332202)
public Diary(Long id, String studentId, String name, String value,
boolean isCurrent) {
this.id = id;
this.studentId = studentId;
this.name = name;
this.value = value;
this.isCurrent = isCurrent;
}
@Generated(hash = 112123061)
public Diary() {
}
public Long getId() {
return id;
}
public Diary setId(Long id) {
this.id = id;
return this;
}
public String getStudentId() {
return studentId;
}
public Diary setStudentId(String studentId) {
this.studentId = studentId;
return this;
}
public String getName() {
return name;
}
public Diary setName(String name) {
this.name = name;
return this;
}
public String getValue() {
return value;
}
public Diary setValue(String value) {
this.value = value;
return this;
}
public boolean getIsCurrent() {
return isCurrent;
}
public Diary setIsCurrent(boolean current) {
isCurrent = current;
return this;
}
public DaoSession getDaoSession() {
return daoSession;
}
public Diary setDaoSession(DaoSession daoSession) {
this.daoSession = daoSession;
return this;
}
public DiaryDao getMyDao() {
return myDao;
}
public Diary setMyDao(DiaryDao myDao) {
this.myDao = myDao;
return this;
}
/**
* 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 = 629297785)
public void __setDaoSession(DaoSession daoSession) {
this.daoSession = daoSession;
myDao = daoSession != null ? daoSession.getDiaryDao() : null;
}
}

View File

@ -10,11 +10,11 @@ import org.greenrobot.greendao.annotation.Property;
import java.io.Serializable;
@Entity(
nameInDb = "Lessons",
nameInDb = "TimetableLessons",
active = true,
indexes = {@Index(value = "dayId,date,startTime,endTime", unique = true)}
)
public class Lesson implements Serializable {
public class TimetableLesson implements Serializable {
@Id(autoincrement = true)
private Long id;
@ -78,14 +78,15 @@ public class Lesson implements Serializable {
/**
* Used for active entity operations.
*/
@Generated(hash = 610143130)
private transient LessonDao myDao;
@Generated(hash = 1119360138)
private transient TimetableLessonDao myDao;
@Generated(hash = 140778287)
public Lesson(Long id, Long dayId, String number, String subject, String teacher, String room,
String description, String groupName, String startTime, String endTime, String date,
boolean isEmpty, boolean isDivisionIntoGroups, boolean isPlanning, boolean isRealized,
boolean isMovedOrCanceled, boolean isNewMovedInOrChanged) {
@Generated(hash = 627457324)
public TimetableLesson(Long id, Long dayId, String number, String subject,
String teacher, String room, String description, String groupName,
String startTime, String endTime, String date, boolean isEmpty,
boolean isDivisionIntoGroups, boolean isPlanning, boolean isRealized,
boolean isMovedOrCanceled, boolean isNewMovedInOrChanged) {
this.id = id;
this.dayId = dayId;
this.number = number;
@ -105,8 +106,8 @@ public class Lesson implements Serializable {
this.isNewMovedInOrChanged = isNewMovedInOrChanged;
}
@Generated(hash = 1669664117)
public Lesson() {
@Generated(hash = 1878030142)
public TimetableLesson() {
}
public Long getId() {
@ -126,186 +127,138 @@ public class Lesson implements Serializable {
}
public String getNumber() {
return number;
return this.number;
}
public Lesson setNumber(String number) {
public TimetableLesson setNumber(String number) {
this.number = number;
return this;
}
public String getSubject() {
return subject;
return this.subject;
}
public Lesson setSubject(String subject) {
public TimetableLesson setSubject(String subject) {
this.subject = subject;
return this;
}
public String getTeacher() {
return teacher;
return this.teacher;
}
public Lesson setTeacher(String teacher) {
public TimetableLesson setTeacher(String teacher) {
this.teacher = teacher;
return this;
}
public String getRoom() {
return room;
return this.room;
}
public Lesson setRoom(String room) {
public TimetableLesson setRoom(String room) {
this.room = room;
return this;
}
public String getDescription() {
return description;
return this.description;
}
public Lesson setDescription(String description) {
public TimetableLesson setDescription(String description) {
this.description = description;
return this;
}
public String getGroupName() {
return groupName;
return this.groupName;
}
public Lesson setGroupName(String groupName) {
public TimetableLesson setGroupName(String groupName) {
this.groupName = groupName;
return this;
}
public String getStartTime() {
return startTime;
return this.startTime;
}
public Lesson setStartTime(String startTime) {
public TimetableLesson setStartTime(String startTime) {
this.startTime = startTime;
return this;
}
public String getEndTime() {
return endTime;
return this.endTime;
}
public Lesson setEndTime(String endTime) {
public TimetableLesson setEndTime(String endTime) {
this.endTime = endTime;
return this;
}
public String getDate() {
return date;
return this.date;
}
public Lesson setDate(String date) {
public TimetableLesson setDate(String date) {
this.date = date;
return this;
}
public boolean isEmpty() {
return isEmpty;
}
public Lesson setEmpty(boolean empty) {
isEmpty = empty;
return this;
}
public boolean isDivisionIntoGroups() {
return isDivisionIntoGroups;
}
public Lesson setDivisionIntoGroups(boolean divisionIntoGroups) {
isDivisionIntoGroups = divisionIntoGroups;
return this;
}
public boolean isPlanning() {
return isPlanning;
}
public Lesson setPlanning(boolean planning) {
isPlanning = planning;
return this;
}
public boolean isRealized() {
return isRealized;
}
public Lesson setRealized(boolean realized) {
isRealized = realized;
return this;
}
public boolean isMovedOrCanceled() {
return isMovedOrCanceled;
}
public Lesson setMovedOrCanceled(boolean movedOrCanceled) {
isMovedOrCanceled = movedOrCanceled;
return this;
}
public boolean isNewMovedInOrChanged() {
return isNewMovedInOrChanged;
}
public Lesson setNewMovedInOrChanged(boolean newMovedInOrChanged) {
isNewMovedInOrChanged = newMovedInOrChanged;
return this;
}
public boolean getIsEmpty() {
return this.isEmpty;
}
public void setIsEmpty(boolean isEmpty) {
public TimetableLesson setEmpty(boolean isEmpty) {
this.isEmpty = isEmpty;
return this;
}
public boolean getIsDivisionIntoGroups() {
return this.isDivisionIntoGroups;
}
public void setIsDivisionIntoGroups(boolean isDivisionIntoGroups) {
public TimetableLesson setDivisionIntoGroups(boolean isDivisionIntoGroups) {
this.isDivisionIntoGroups = isDivisionIntoGroups;
return this;
}
public boolean getIsPlanning() {
return this.isPlanning;
}
public void setIsPlanning(boolean isPlanning) {
public TimetableLesson setPlanning(boolean isPlanning) {
this.isPlanning = isPlanning;
return this;
}
public boolean getIsRealized() {
return this.isRealized;
}
public void setIsRealized(boolean isRealized) {
public TimetableLesson setRealized(boolean isRealized) {
this.isRealized = isRealized;
return this;
}
public boolean getIsMovedOrCanceled() {
return this.isMovedOrCanceled;
}
public void setIsMovedOrCanceled(boolean isMovedOrCanceled) {
public TimetableLesson setMovedOrCanceled(boolean isMovedOrCanceled) {
this.isMovedOrCanceled = isMovedOrCanceled;
return this;
}
public boolean getIsNewMovedInOrChanged() {
return this.isNewMovedInOrChanged;
}
public void setIsNewMovedInOrChanged(boolean isNewMovedInOrChanged) {
public TimetableLesson setNewMovedInOrChanged(boolean isNewMovedInOrChanged) {
this.isNewMovedInOrChanged = isNewMovedInOrChanged;
return this;
}
/**
@ -344,10 +297,34 @@ public class Lesson implements Serializable {
myDao.update(this);
}
public void setIsEmpty(boolean isEmpty) {
this.isEmpty = isEmpty;
}
public void setIsDivisionIntoGroups(boolean isDivisionIntoGroups) {
this.isDivisionIntoGroups = isDivisionIntoGroups;
}
public void setIsPlanning(boolean isPlanning) {
this.isPlanning = isPlanning;
}
public void setIsRealized(boolean isRealized) {
this.isRealized = isRealized;
}
public void setIsMovedOrCanceled(boolean isMovedOrCanceled) {
this.isMovedOrCanceled = isMovedOrCanceled;
}
public void setIsNewMovedInOrChanged(boolean isNewMovedInOrChanged) {
this.isNewMovedInOrChanged = isNewMovedInOrChanged;
}
/** called by internal mechanisms, do not call yourself. */
@Generated(hash = 2078826279)
@Generated(hash = 1885258429)
public void __setDaoSession(DaoSession daoSession) {
this.daoSession = daoSession;
myDao = daoSession != null ? daoSession.getLessonDao() : null;
myDao = daoSession != null ? daoSession.getTimetableLessonDao() : null;
}
}

View File

@ -26,6 +26,12 @@ public class Week {
@Property(nameInDb = "START_DATE")
private String startDayDate = "";
@Property(nameInDb = "IS_ATTENDANCE_SYNCED")
private boolean isAttendanceSynced = false;
@Property(nameInDb = "IS_TIMETABLE_SYNCED")
private boolean isTimetableSynced = false;
@ToMany(referencedJoinProperty = "weekId")
private List<Day> dayList;
@ -37,11 +43,14 @@ public class Week {
@Generated(hash = 1019310398)
private transient WeekDao myDao;
@Generated(hash = 36829814)
public Week(Long id, Long userId, String startDayDate) {
@Generated(hash = 1745118398)
public Week(Long id, Long userId, String startDayDate, boolean isAttendanceSynced,
boolean isTimetableSynced) {
this.id = id;
this.userId = userId;
this.startDayDate = startDayDate;
this.isAttendanceSynced = isAttendanceSynced;
this.isTimetableSynced = isTimetableSynced;
}
@Generated(hash = 2135529658)
@ -75,6 +84,22 @@ public class Week {
return this;
}
public boolean getIsAttendanceSynced() {
return this.isAttendanceSynced;
}
public void setIsAttendanceSynced(boolean isAttendanceSynced) {
this.isAttendanceSynced = isAttendanceSynced;
}
public boolean getIsTimetableSynced() {
return this.isTimetableSynced;
}
public void setIsTimetableSynced(boolean isTimetableSynced) {
this.isTimetableSynced = isTimetableSynced;
}
/**
* To-many relationship, resolved on first access (and after reset).
* Changes to to-many relations are not persisted, make changes to the target entity.

View File

@ -0,0 +1,114 @@
package io.github.wulkanowy.data.db.dao.migrations;
import android.database.Cursor;
import android.os.AsyncTask;
import org.greenrobot.greendao.database.Database;
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.data.db.dao.DbHelper;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.security.Scrambler;
public class Migration23 implements DbHelper.Migration {
@Override
public Integer getVersion() {
return 23;
}
@Override
public void runMigration(final Database db, final SharedPrefContract sharedPref, final Vulcan vulcan) throws Exception {
createDiaryTable(db);
migrateAccountsTable(db);
final Map<String, String> user = getAccountData(db);
vulcan.setCredentials(
user.get("email"),
Scrambler.decrypt(user.get("email"), user.get("password")),
user.get("symbol"),
user.get("school_id"),
"", // inserted in code bellow
""
);
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
insertDiaries(db, vulcan.getStudentAndParent().getDiaries());
updateAccount(db, vulcan.getStudentAndParent().getStudentID());
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private void createDiaryTable(Database db) {
db.execSQL("DROP TABLE IF EXISTS Diaries");
db.execSQL("CREATE TABLE IF NOT EXISTS \"Diaries\" (" + //
"\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id
"\"STUDENT_ID\" TEXT," + // 1: studentId
"\"NAME\" TEXT," + // 2: name
"\"VALUE\" TEXT," + // 3: value
"\"IS_CURRENT\" INTEGER NOT NULL );"); // 4: isCurrent
}
private void migrateAccountsTable(Database db) {
db.execSQL("DROP TABLE IF EXISTS tmp_account");
db.execSQL("ALTER TABLE Accounts RENAME TO tmp_account");
db.execSQL("CREATE TABLE IF NOT EXISTS \"Accounts\" (" + //
"\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id
"\"REAL_ID\" TEXT," + // 1: realId
"\"SYMBOL\" TEXT," + // 2: symbol
"\"SCHOOL_ID\" TEXT," + // 3: schoolId
"\"NAME\" TEXT," + // 4: name
"\"E_MAIL\" TEXT," + // 5: email
"\"PASSWORD\" TEXT);"); // 6: password
// Add Indexes
db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS IDX_Accounts_REAL_ID ON \"Accounts\" (\"REAL_ID\" ASC);");
db.execSQL("INSERT INTO Accounts(NAME, E_MAIL, PASSWORD, SYMBOL, SCHOOL_ID)" +
"SELECT `NAME`, `E-MAIL`, `PASSWORD`, `SYMBOL`, `SNPID` FROM tmp_account");
db.execSQL("DROP TABLE tmp_account");
}
private Map<String, String> getAccountData(Database db) {
Map<String, String> values = new HashMap<>();
Cursor cursor = db.rawQuery("SELECT SYMBOL, SCHOOL_ID, NAME, E_MAIL, PASSWORD FROM Accounts", null);
if (cursor.moveToFirst()) {
do {
values.put("symbol", cursor.getString(cursor.getColumnIndex("SYMBOL")));
values.put("school_id", cursor.getString(cursor.getColumnIndex("SCHOOL_ID")));
values.put("name", cursor.getString(cursor.getColumnIndex("NAME")));
values.put("email", cursor.getString(cursor.getColumnIndex("E_MAIL")));
values.put("password", cursor.getString(cursor.getColumnIndex("PASSWORD")));
} while (cursor.moveToNext());
}
cursor.close();
return values;
}
private void insertDiaries(Database db, List<Diary> list) {
for (Diary diary : list) {
db.execSQL("INSERT INTO Diaries(STUDENT_ID, NAME, VALUE, IS_CURRENT) VALUES(" +
"\"" + diary.getStudentId() + "\"," +
"\"" + diary.getName() + "\"," +
"\"" + diary.getId() + "\"," +
"\"" + (diary.isCurrent() ? "1" : "0") + "\"" +
")");
}
}
private void updateAccount(Database db, String realId) {
db.execSQL("UPDATE Accounts SET REAL_ID = ?", new String[]{realId});
}
}

View File

@ -3,6 +3,8 @@ 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;
@ -11,8 +13,8 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.R;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException;
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;
@ -41,6 +43,7 @@ public class AppResources implements ResourcesContract {
@Override
public String getErrorLoginMessage(Exception exception) {
LogUtils.error(AppConstant.APP_NAME + " encountered a error", exception);
Crashlytics.logException(exception);
if (exception instanceof CryptoException) {
return resources.getString(R.string.encrypt_failed_text);
@ -50,10 +53,39 @@ public class AppResources implements ResourcesContract {
return resources.getString(R.string.generic_timeout_error);
} else if (exception instanceof NotLoggedInErrorException || exception instanceof IOException) {
return resources.getString(R.string.login_denied_text);
} else if (exception instanceof VulcanOfflineException) {
return resources.getString(R.string.error_host_offline);
} else {
return exception.getMessage();
}
}
@Override
public String getAttendanceLessonDescription(AttendanceLesson lesson) {
int id = R.string.attendance_present;
if (lesson.getIsAbsenceForSchoolReasons()) {
id = R.string.attendance_absence_for_school_reasons;
}
if (lesson.getIsAbsenceExcused()) {
id = R.string.attendance_absence_excused;
}
if (lesson.getIsAbsenceUnexcused()) {
id = R.string.attendance_absence_unexcused;
}
if (lesson.getIsExemption()) {
id = R.string.attendance_exemption;
}
if (lesson.getIsExcusedLateness()) {
id = R.string.attendance_excused_lateness;
}
if (lesson.getIsUnexcusedLateness()) {
id = R.string.attendance_unexcused_lateness;
}
return resources.getString(id);
}
}

View File

@ -1,5 +1,7 @@
package io.github.wulkanowy.data.db.resources;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
public interface ResourcesContract {
String[] getSymbolsKeysArray();
@ -7,4 +9,6 @@ public interface ResourcesContract {
String[] getSymbolsValuesArray();
String getErrorLoginMessage(Exception e);
String getAttendanceLessonDescription(AttendanceLesson lesson);
}

View File

@ -2,32 +2,62 @@ package io.github.wulkanowy.data.db.shared;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import javax.inject.Inject;
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
public class SharedPref implements SharedPrefContract {
private static final String SHARED_KEY_USER_ID = "USER_ID";
private final SharedPreferences sharedPreferences;
private final SharedPreferences appSharedPref;
private final SharedPreferences settingsSharedPref;
@Inject
SharedPref(@ApplicationContext Context context, @SharedPreferencesInfo String sharedName) {
sharedPreferences = context.getSharedPreferences(sharedName, Context.MODE_PRIVATE);
appSharedPref = context.getSharedPreferences(sharedName, Context.MODE_PRIVATE);
settingsSharedPref = PreferenceManager.getDefaultSharedPreferences(context);
}
@Override
public long getCurrentUserId() {
return sharedPreferences.getLong(SHARED_KEY_USER_ID, 0);
return appSharedPref.getLong(SHARED_KEY_USER_ID, 0);
}
@Override
public void setCurrentUserId(long userId) {
sharedPreferences.edit().putLong(SHARED_KEY_USER_ID, userId).apply();
appSharedPref.edit().putLong(SHARED_KEY_USER_ID, userId).apply();
}
@Override
public int getStartupTab() {
return Integer.parseInt(settingsSharedPref.getString(SettingsFragment.SHARED_KEY_START_TAB, "2"));
}
@Override
public int getServicesInterval() {
return Integer.parseInt(settingsSharedPref.getString(SettingsFragment.SHARED_KEY_SERVICES_INTERVAL, "60"));
}
@Override
public boolean isServicesEnable() {
return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_SERVICES_ENABLE, true);
}
@Override
public boolean isNotifyEnable() {
return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_NOTIFY_ENABLE, true);
}
@Override
public boolean isMobileDisable() {
return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_SERVICES_MOBILE_DISABLED, false);
}
}

View File

@ -5,4 +5,14 @@ public interface SharedPrefContract {
long getCurrentUserId();
void setCurrentUserId(long userId);
int getStartupTab();
int getServicesInterval();
boolean isMobileDisable();
boolean isServicesEnable();
boolean isNotifyEnable();
}

View File

@ -3,9 +3,9 @@ package io.github.wulkanowy.data.sync;
import java.io.IOException;
import java.text.ParseException;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.VulcanException;
public interface SyncContract {
void sync() throws NotLoggedInErrorException, IOException, ParseException;
void sync() throws VulcanException, IOException, ParseException;
}

View File

@ -0,0 +1,91 @@
package io.github.wulkanowy.data.sync.account;
import android.content.Context;
import java.io.IOException;
import java.util.List;
import javax.inject.Inject;
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.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.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;
@Singleton
public class AccountSync implements AccountSyncContract {
private final DaoSession daoSession;
private final SharedPrefContract sharedPref;
private final Vulcan vulcan;
private final Context context;
@Inject
AccountSync(DaoSession daoSession, SharedPrefContract sharedPref,
Vulcan vulcan, @ApplicationContext Context context) {
this.daoSession = daoSession;
this.sharedPref = sharedPref;
this.vulcan = vulcan;
this.context = context;
}
@Override
public void registerUser(String email, String password, String symbol)
throws VulcanException, IOException, CryptoException {
LogUtils.debug("Register new user email=" + email);
vulcan.setCredentials(email, password, symbol, null, null, null);
Account account = new Account()
.setName(vulcan.getBasicInformation().getPersonalData().getFirstAndLastName())
.setEmail(email)
.setPassword(Scrambler.encrypt(email, password, context))
.setSymbol(vulcan.getSymbol())
.setSchoolId(vulcan.getStudentAndParent().getSchoolID())
.setRealId(vulcan.getStudentAndParent().getStudentID());
List<Diary> diaryList = DataObjectConverter.diariesToDiaryEntities(
vulcan.getStudentAndParent().getDiaries());
daoSession.getAccountDao().insert(account);
daoSession.getDiaryDao().insertInTx(diaryList);
sharedPref.setCurrentUserId(account.getId());
}
@Override
public void initLastUser() throws IOException, CryptoException {
long userId = sharedPref.getCurrentUserId();
if (userId == 0) {
throw new IOException("Can't find saved user");
}
LogUtils.debug("Initialization current user id=" + userId);
Account account = daoSession.getAccountDao().load(userId);
vulcan.setCredentials(account.getEmail(),
Scrambler.decrypt(account.getEmail(), account.getPassword()),
account.getSymbol(),
account.getSchoolId(),
account.getRealId(),
daoSession.getDiaryDao().queryBuilder()
.where(DiaryDao.Properties.IsCurrent.eq(true)).unique().getValue()
);
}
}

View File

@ -0,0 +1,16 @@
package io.github.wulkanowy.data.sync.account;
import java.io.IOException;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.utils.security.CryptoException;
public interface AccountSyncContract {
void registerUser(String email, String password, String symbol)
throws VulcanException, IOException,
CryptoException;
void initLastUser() throws VulcanException, IOException,
CryptoException;
}

View File

@ -0,0 +1,156 @@
package io.github.wulkanowy.data.sync.attendance;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.api.generic.Lesson;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLessonDao;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Day;
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.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.TimeUtils;
@Singleton
public class AttendanceSync implements AttendanceSyncContract {
private final DaoSession daoSession;
private final SharedPrefContract sharedPref;
private final Vulcan vulcan;
private long userId;
@Inject
AttendanceSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) {
this.daoSession = daoSession;
this.sharedPref = sharedPref;
this.vulcan = vulcan;
}
@Override
public void syncAttendance() throws IOException, ParseException, VulcanException {
syncAttendance(null);
}
@Override
public void syncAttendance(String date) throws IOException, ParseException, VulcanException {
this.userId = sharedPref.getCurrentUserId();
io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> weekApi = getWeekFromApi(getNormalizedDate(date));
Week weekDb = getWeekFromDb(weekApi.getStartDayDate());
long weekId = updateWeekInDb(weekDb, weekApi);
List<AttendanceLesson> lessonList = updateDays(weekApi.getDays(), weekId);
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)) : "";
}
private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> getWeekFromApi(String date)
throws IOException, ParseException, VulcanException {
return vulcan.getAttendanceTable().getWeekTable(date);
}
private Week getWeekFromDb(String date) {
return daoSession.getWeekDao()
.queryBuilder()
.where(WeekDao.Properties.UserId.eq(userId), WeekDao.Properties.StartDayDate.eq(date))
.unique();
}
private Long updateWeekInDb(Week dbWeekEntity, io.github.wulkanowy.api.generic.Week fromApi) {
if (dbWeekEntity != null) {
dbWeekEntity.setIsAttendanceSynced(true);
dbWeekEntity.update();
return dbWeekEntity.getId();
}
Week apiWeekEntity = DataObjectConverter.weekToWeekEntity(fromApi).setUserId(userId);
apiWeekEntity.setIsAttendanceSynced(true);
return daoSession.getWeekDao().insert(apiWeekEntity);
}
private List<AttendanceLesson> updateDays(List<io.github.wulkanowy.api.generic.Day> dayListFromApi, long weekId) {
List<AttendanceLesson> updatedLessonList = new ArrayList<>();
for (io.github.wulkanowy.api.generic.Day dayFromApi : dayListFromApi) {
Day dbDayEntity = getDayFromDb(dayFromApi.getDate());
Day apiDayEntity = DataObjectConverter.dayToDayEntity(dayFromApi);
long dayId = updateDay(dbDayEntity, apiDayEntity, weekId);
updateLessons(dayFromApi.getLessons(), updatedLessonList, dayId);
}
return updatedLessonList;
}
private Day getDayFromDb(String date) {
return daoSession.getDayDao()
.queryBuilder()
.where(DayDao.Properties.UserId.eq(userId), DayDao.Properties.Date.eq(date))
.unique();
}
private long updateDay(Day dbDayEntity, Day apiDayEntity, long weekId) {
if (null != dbDayEntity) {
return dbDayEntity.getId();
}
apiDayEntity.setUserId(userId);
apiDayEntity.setWeekId(weekId);
return daoSession.getDayDao().insert(apiDayEntity);
}
private void updateLessons(List<Lesson> lessons, List<AttendanceLesson> updatedLessons, long dayId) {
List<AttendanceLesson> lessonsFromApiEntities = DataObjectConverter
.lessonsToAttendanceLessonsEntities(lessons);
for (AttendanceLesson apiLessonEntity : lessonsFromApiEntities) {
AttendanceLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId);
apiLessonEntity.setDayId(dayId);
if (lessonFromDb != null) {
apiLessonEntity.setId(lessonFromDb.getId());
}
if (!"".equals(apiLessonEntity.getSubject())) {
updatedLessons.add(apiLessonEntity);
}
}
}
private AttendanceLesson getLessonFromDb(AttendanceLesson apiEntity, long dayId) {
return daoSession.getAttendanceLessonDao().queryBuilder()
.where(AttendanceLessonDao.Properties.DayId.eq(dayId),
AttendanceLessonDao.Properties.Date.eq(apiEntity.getDate()),
AttendanceLessonDao.Properties.Number.eq(apiEntity.getNumber()))
.unique();
}
}

View File

@ -0,0 +1,13 @@
package io.github.wulkanowy.data.sync.attendance;
import java.io.IOException;
import java.text.ParseException;
import io.github.wulkanowy.api.VulcanException;
public interface AttendanceSyncContract {
void syncAttendance(String date) throws IOException, ParseException, VulcanException;
void syncAttendance() throws IOException, ParseException, VulcanException;
}

View File

@ -9,7 +9,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Grade;
@ -29,6 +29,8 @@ public class GradeSync implements SyncContract {
private final SharedPrefContract sharedPref;
private Long userId;
@Inject
GradeSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) {
this.daoSession = daoSession;
@ -37,36 +39,53 @@ public class GradeSync implements SyncContract {
}
@Override
public void sync() throws IOException, NotLoggedInErrorException, ParseException {
public void sync() throws IOException, VulcanException, ParseException {
long userId = sharedPref.getCurrentUserId();
userId = sharedPref.getCurrentUserId();
Account account = daoSession.getAccountDao().load(userId);
account.resetGradeList();
account.resetSubjectList();
resetAccountRelations(account);
List<Grade> gradesFromNet = DataObjectConverter
.gradesToGradeEntities(vulcan.getGradesList().getAll());
List<Grade> gradesFromDb = account.getGradeList();
List<Grade> updatedGrades = EntitiesCompare.compareGradeList(gradesFromNet, gradesFromDb);
daoSession.getGradeDao().deleteInTx(gradesFromDb);
List<Grade> lastList = new ArrayList<>();
for (Grade grade : updatedGrades) {
grade.setUserId(userId);
grade.setSubjectId(daoSession.getSubjectDao().queryBuilder()
.where(SubjectDao.Properties.Name.eq(grade.getSubject()),
SubjectDao.Properties.UserId.eq(userId))
.build()
.uniqueOrThrow().getId());
lastList.add(grade);
}
List<Grade> lastList = getUpdatedList(getComparedList(account));
daoSession.getGradeDao().deleteInTx(account.getGradeList());
daoSession.getGradeDao().insertInTx(lastList);
LogUtils.debug("Synchronization grades (amount = " + lastList.size() + ")");
}
private void resetAccountRelations(Account account) {
account.resetSubjectList();
account.resetGradeList();
}
private List<Grade> getUpdatedList(List<Grade> comparedList) {
List<Grade> updatedList = new ArrayList<>();
for (Grade grade : comparedList) {
grade.setUserId(userId);
grade.setSubjectId(getSubjectId(grade.getSubject()));
updatedList.add(grade);
}
return updatedList;
}
private List<Grade> getComparedList(Account account) throws IOException, VulcanException,
ParseException {
List<Grade> gradesFromNet = DataObjectConverter
.gradesToGradeEntities(vulcan.getGradesList().getAll());
List<Grade> gradesFromDb = account.getGradeList();
return EntitiesCompare.compareGradeList(gradesFromNet, gradesFromDb);
}
private Long getSubjectId(String subjectName) {
return daoSession.getSubjectDao().queryBuilder()
.where(SubjectDao.Properties.Name.eq(subjectName),
SubjectDao.Properties.UserId.eq(userId))
.build()
.uniqueOrThrow()
.getId();
}
}

View File

@ -1,81 +0,0 @@
package io.github.wulkanowy.data.sync.login;
import android.content.Context;
import java.io.IOException;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.login.AccountPermissionException;
import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException;
import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.security.CryptoException;
import io.github.wulkanowy.utils.security.Scrambler;
@Singleton
public class LoginSync implements LoginSyncContract {
private final DaoSession daoSession;
private final SharedPrefContract sharedPref;
private final Vulcan vulcan;
private final Context context;
@Inject
LoginSync(DaoSession daoSession, SharedPrefContract sharedPref,
Vulcan vulcan, @ApplicationContext Context context) {
this.daoSession = daoSession;
this.sharedPref = sharedPref;
this.vulcan = vulcan;
this.context = context;
}
@Override
public void loginUser(String email, String password, String symbol)
throws NotLoggedInErrorException, AccountPermissionException, IOException,
CryptoException, VulcanOfflineException, BadCredentialsException {
LogUtils.debug("Login new user email=" + email);
vulcan.login(email, password, symbol);
Account account = new Account()
.setName(vulcan.getBasicInformation().getPersonalData().getFirstAndLastName())
.setEmail(email)
.setPassword(Scrambler.encrypt(email, password, context))
.setSymbol(vulcan.getSymbol())
.setSnpId(vulcan.getStudentAndParent().getId());
sharedPref.setCurrentUserId(daoSession.getAccountDao().insert(account));
}
@Override
public void loginCurrentUser() throws NotLoggedInErrorException, AccountPermissionException,
IOException, CryptoException, VulcanOfflineException, BadCredentialsException {
long userId = sharedPref.getCurrentUserId();
if (userId == 0) {
throw new IOException("Can't find logged user");
}
LogUtils.debug("Login current user id=" + userId);
Account account = daoSession.getAccountDao().load(userId);
vulcan.login(account.getEmail(),
Scrambler.decrypt(account.getEmail(), account.getPassword()),
account.getSymbol(),
account.getSnpId());
}
}

View File

@ -1,19 +0,0 @@
package io.github.wulkanowy.data.sync.login;
import java.io.IOException;
import io.github.wulkanowy.api.login.AccountPermissionException;
import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException;
import io.github.wulkanowy.utils.security.CryptoException;
public interface LoginSyncContract {
void loginUser(String email, String password, String symbol)
throws NotLoggedInErrorException, AccountPermissionException, IOException,
CryptoException, VulcanOfflineException, BadCredentialsException;
void loginCurrentUser() throws NotLoggedInErrorException, AccountPermissionException, IOException,
CryptoException, VulcanOfflineException, BadCredentialsException;
}

View File

@ -9,10 +9,10 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.data.db.dao.entities.SubjectDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.data.sync.SyncContract;
import io.github.wulkanowy.utils.DataObjectConverter;
@ -21,41 +21,51 @@ import io.github.wulkanowy.utils.LogUtils;
@Singleton
public class SubjectSync implements SyncContract {
private final SubjectDao subjectDao;
private final DaoSession daoSession;
private final Vulcan vulcan;
private final SharedPrefContract sharedPref;
private Long userId;
@Inject
SubjectSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) {
this.subjectDao = daoSession.getSubjectDao();
this.daoSession = daoSession;
this.sharedPref = sharedPref;
this.vulcan = vulcan;
}
@Override
public void sync() throws NotLoggedInErrorException, IOException, ParseException {
public void sync() throws VulcanException, IOException, ParseException {
long userId = sharedPref.getCurrentUserId();
userId = sharedPref.getCurrentUserId();
List<Subject> subjectsFromNet = DataObjectConverter
.subjectsToSubjectEntities(vulcan.getSubjectsList().getAll());
List<Subject> lastList = getUpdatedList(getSubjectsFromNet());
subjectDao.deleteInTx(subjectDao.queryBuilder()
.where(SubjectDao.Properties.UserId.eq(userId))
.build()
.list());
List<Subject> lastList = new ArrayList<>();
for (Subject subject : subjectsFromNet) {
subject.setUserId(userId);
lastList.add(subject);
}
subjectDao.insertInTx(lastList);
daoSession.getSubjectDao().deleteInTx(getSubjectsFromDb());
daoSession.getSubjectDao().insertInTx(lastList);
LogUtils.debug("Synchronization subjects (amount = " + lastList.size() + ")");
}
private List<Subject> getSubjectsFromNet() throws VulcanException, IOException {
return DataObjectConverter.subjectsToSubjectEntities(vulcan.getSubjectsList().getAll());
}
private List<Subject> getSubjectsFromDb() {
Account account = daoSession.getAccountDao().load(userId);
account.resetSubjectList();
return account.getSubjectList();
}
private List<Subject> getUpdatedList(List<Subject> subjectsFromNet) {
List<Subject> updatedList = new ArrayList<>();
for (Subject subject : subjectsFromNet) {
subject.setUserId(userId);
updatedList.add(subject);
}
return updatedList;
}
}

View File

@ -9,12 +9,13 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.api.generic.Lesson;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Day;
import io.github.wulkanowy.data.db.dao.entities.DayDao;
import io.github.wulkanowy.data.db.dao.entities.Lesson;
import io.github.wulkanowy.data.db.dao.entities.LessonDao;
import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
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.data.db.shared.SharedPrefContract;
@ -27,9 +28,11 @@ public class TimetableSync implements TimetableSyncContract {
private final DaoSession daoSession;
private final SharedPrefContract sharedPref;
private final Vulcan vulcan;
private final SharedPrefContract sharedPref;
private long userId;
@Inject
TimetableSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) {
@ -39,82 +42,121 @@ public class TimetableSync implements TimetableSyncContract {
}
@Override
public void syncTimetable(String date) throws NotLoggedInErrorException, IOException, ParseException {
long userId = sharedPref.getCurrentUserId();
io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> weekFromNet =
date == null ? vulcan.getTimetable().getWeekTable()
: vulcan.getTimetable().getWeekTable(String.valueOf(TimeUtils.getNetTicks(date)));
Week weekFromDb = daoSession.getWeekDao().queryBuilder()
.where(WeekDao.Properties.UserId.eq(userId),
WeekDao.Properties.StartDayDate.eq(weekFromNet.getStartDayDate()))
.unique();
Long weekId;
if (weekFromDb == null) {
Week weekFromNetEntity = DataObjectConverter.weekToWeekEntity(weekFromNet).setUserId(userId);
weekId = daoSession.getWeekDao().insert(weekFromNetEntity);
} else {
weekId = weekFromDb.getId();
}
List<io.github.wulkanowy.api.generic.Day> dayListFromNet = weekFromNet.getDays();
List<Lesson> updatedLessonList = new ArrayList<>();
for (io.github.wulkanowy.api.generic.Day dayFromNet : dayListFromNet) {
Day dayFromNetEntity = DataObjectConverter.dayToDayEntity(dayFromNet);
Day dayFromDb = daoSession.getDayDao().queryBuilder()
.where(DayDao.Properties.UserId.eq(userId),
DayDao.Properties.WeekId.eq(weekId),
DayDao.Properties.Date.eq(dayFromNetEntity.getDate()))
.unique();
dayFromNetEntity.setUserId(userId);
dayFromNetEntity.setWeekId(weekId);
Long dayId;
if (dayFromDb != null) {
dayFromNetEntity.setId(dayFromDb.getId());
daoSession.getDayDao().save(dayFromNetEntity);
dayId = dayFromNetEntity.getId();
} else {
dayId = daoSession.getDayDao().insert(dayFromNetEntity);
}
List<Lesson> lessonListFromNetEntities = DataObjectConverter
.lessonsToLessonsEntities(dayFromNet.getLessons());
for (Lesson lessonFromNetEntity : lessonListFromNetEntities) {
Lesson lessonFromDb = daoSession.getLessonDao().queryBuilder()
.where(LessonDao.Properties.DayId.eq(dayId),
LessonDao.Properties.Date.eq(lessonFromNetEntity.getDate()),
LessonDao.Properties.StartTime.eq(lessonFromNetEntity.getStartTime()),
LessonDao.Properties.EndTime.eq(lessonFromNetEntity.getEndTime()))
.unique();
if (lessonFromDb != null) {
lessonFromNetEntity.setId(lessonFromDb.getId());
}
lessonFromNetEntity.setDayId(dayFromNetEntity.getId());
if (!"".equals(lessonFromNetEntity.getSubject())) {
updatedLessonList.add(lessonFromNetEntity);
}
}
}
daoSession.getLessonDao().saveInTx(updatedLessonList);
LogUtils.debug("Synchronization lessons (amount = " + updatedLessonList.size() + ")");
public void syncTimetable() throws IOException, ParseException, VulcanException {
syncTimetable(null);
}
@Override
public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException {
syncTimetable(null);
public void syncTimetable(String date) throws IOException, ParseException, VulcanException {
this.userId = sharedPref.getCurrentUserId();
io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> weekApi = getWeekFromApi(getNormalizedDate(date));
Week weekDb = getWeekFromDb(weekApi.getStartDayDate());
long weekId = updateWeekInDb(weekDb, weekApi);
List<TimetableLesson> lessonList = updateDays(weekApi.getDays(), weekId);
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)) : "";
}
private io.github.wulkanowy.api.generic.Week<io.github.wulkanowy.api.generic.Day> getWeekFromApi(String date)
throws IOException, VulcanException, ParseException {
return vulcan.getTimetable().getWeekTable(date);
}
private Week getWeekFromDb(String date) {
return daoSession.getWeekDao()
.queryBuilder()
.where(WeekDao.Properties.UserId.eq(userId), WeekDao.Properties.StartDayDate.eq(date))
.unique();
}
private Long updateWeekInDb(Week dbEntity, io.github.wulkanowy.api.generic.Week fromApi) {
if (dbEntity != null) {
dbEntity.setIsTimetableSynced(true);
dbEntity.update();
return dbEntity.getId();
}
Week apiEntity = DataObjectConverter.weekToWeekEntity(fromApi).setUserId(userId);
apiEntity.setIsTimetableSynced(true);
return daoSession.getWeekDao().insert(apiEntity);
}
private List<TimetableLesson> updateDays(List<io.github.wulkanowy.api.generic.Day> dayListFromApi, long weekId) {
List<TimetableLesson> updatedLessonList = new ArrayList<>();
for (io.github.wulkanowy.api.generic.Day dayFromApi : dayListFromApi) {
Day dbDayEntity = getDayFromDb(dayFromApi.getDate());
Day apiDayEntity = DataObjectConverter.dayToDayEntity(dayFromApi);
long dayId = updateDay(dbDayEntity, apiDayEntity, weekId);
updateLessons(dayFromApi.getLessons(), updatedLessonList, dayId);
}
return updatedLessonList;
}
private Day getDayFromDb(String date) {
return daoSession.getDayDao()
.queryBuilder()
.where(DayDao.Properties.UserId.eq(userId), DayDao.Properties.Date.eq(date))
.unique();
}
private long updateDay(Day dayFromDb, Day apiDayEntity, long weekId) {
apiDayEntity.setUserId(userId);
apiDayEntity.setWeekId(weekId);
if (null != dayFromDb) {
apiDayEntity.setId(dayFromDb.getId());
daoSession.getDayDao().save(apiDayEntity);
dayFromDb.refresh();
return dayFromDb.getId();
}
return daoSession.getDayDao().insert(apiDayEntity);
}
private void updateLessons(List<Lesson> lessons, List<TimetableLesson> updatedLessons, long dayId) {
List<TimetableLesson> lessonsFromApiEntities = DataObjectConverter
.lessonsToTimetableLessonsEntities(lessons);
for (TimetableLesson apiLessonEntity : lessonsFromApiEntities) {
TimetableLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId);
apiLessonEntity.setDayId(dayId);
if (lessonFromDb != null) {
apiLessonEntity.setId(lessonFromDb.getId());
}
if (!"".equals(apiLessonEntity.getSubject())) {
updatedLessons.add(apiLessonEntity);
}
}
}
private TimetableLesson getLessonFromDb(TimetableLesson apiEntity, long dayId) {
return daoSession.getTimetableLessonDao().queryBuilder()
.where(TimetableLessonDao.Properties.DayId.eq(dayId),
TimetableLessonDao.Properties.Date.eq(apiEntity.getDate()),
TimetableLessonDao.Properties.StartTime.eq(apiEntity.getStartTime()),
TimetableLessonDao.Properties.EndTime.eq(apiEntity.getEndTime()))
.unique();
}
}

View File

@ -3,11 +3,11 @@ package io.github.wulkanowy.data.sync.timetable;
import java.io.IOException;
import java.text.ParseException;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.VulcanException;
public interface TimetableSyncContract {
void syncTimetable(String date) throws NotLoggedInErrorException, IOException, ParseException;
void syncTimetable(String date) throws VulcanException, IOException, ParseException;
void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException;
void syncTimetable() throws VulcanException, IOException, ParseException;
}

View File

@ -4,6 +4,7 @@ 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.dashboard.DashboardFragment;
import io.github.wulkanowy.ui.main.grades.GradesFragment;
import io.github.wulkanowy.ui.main.timetable.TimetableFragment;
@ -17,6 +18,8 @@ public interface FragmentComponent {
void inject(AttendanceFragment attendanceFragment);
void inject(AttendanceTabFragment attendanceTabFragment);
void inject(DashboardFragment dashboardFragment);
void inject(TimetableFragment timetableFragment);

View File

@ -21,9 +21,11 @@ import io.github.wulkanowy.data.db.resources.ResourcesContract;
import io.github.wulkanowy.data.db.shared.SharedPref;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.data.sync.SyncContract;
import io.github.wulkanowy.data.sync.account.AccountSync;
import io.github.wulkanowy.data.sync.account.AccountSyncContract;
import io.github.wulkanowy.data.sync.attendance.AttendanceSync;
import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract;
import io.github.wulkanowy.data.sync.grades.GradeSync;
import io.github.wulkanowy.data.sync.login.LoginSync;
import io.github.wulkanowy.data.sync.login.LoginSyncContract;
import io.github.wulkanowy.data.sync.subjects.SubjectSync;
import io.github.wulkanowy.data.sync.timetable.TimetableSync;
import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract;
@ -98,8 +100,8 @@ public class ApplicationModule {
@Singleton
@Provides
LoginSyncContract provideLoginSync(LoginSync loginSync) {
return loginSync;
AccountSyncContract provideLoginSync(AccountSync accountSync) {
return accountSync;
}
@SyncGrades
@ -122,6 +124,12 @@ public class ApplicationModule {
return timetableSync;
}
@Singleton
@Provides
AttendanceSyncContract provideAttendanceSync(AttendanceSync attendanceSync) {
return attendanceSync;
}
@Provides
FirebaseJobDispatcher provideDispatcher() {
return new FirebaseJobDispatcher(new GooglePlayDriver(application));

View File

@ -7,7 +7,11 @@ import dagger.Provides;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import io.github.wulkanowy.di.annotations.PerFragment;
import io.github.wulkanowy.ui.main.attendance.AttendanceContract;
import io.github.wulkanowy.ui.main.attendance.AttendanceHeaderItem;
import io.github.wulkanowy.ui.main.attendance.AttendancePagerAdapter;
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.dashboard.DashboardContract;
import io.github.wulkanowy.ui.main.dashboard.DashboardPresenter;
import io.github.wulkanowy.ui.main.grades.GradeHeaderItem;
@ -47,6 +51,22 @@ public class FragmentModule {
return dashboardPresenter;
}
@PerFragment
@Provides
AttendanceTabContract.Presenter provideAttendanceTabPresenter(AttendanceTabPresenter timetableTabPresenter) {
return timetableTabPresenter;
}
@Provides
AttendancePagerAdapter provideAttendancePagerAdapter() {
return new AttendancePagerAdapter(fragment.getChildFragmentManager());
}
@Provides
FlexibleAdapter<AttendanceHeaderItem> provideAttendanceTabAdapter() {
return new FlexibleAdapter<>(null);
}
@PerFragment
@Provides
TimetableContract.Presenter provideTimetablePresenter(TimetablePresenter timetablePresenter) {
@ -65,12 +85,12 @@ public class FragmentModule {
}
@Provides
FlexibleAdapter<GradeHeaderItem> provideGradesAdapter() {
FlexibleAdapter<TimetableHeaderItem> provideTimetableTabAdapter() {
return new FlexibleAdapter<>(null);
}
@Provides
FlexibleAdapter<TimetableHeaderItem> provideTimetableTabAdapter() {
FlexibleAdapter<GradeHeaderItem> provideGradesAdapter() {
return new FlexibleAdapter<>(null);
}
}

View File

@ -0,0 +1,36 @@
package io.github.wulkanowy.services;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import io.github.wulkanowy.R;
class GradeNotify extends NotificationService {
private static final String CHANNEL_ID = "Grade_Notify";
GradeNotify(Context context) {
super(context);
}
@Override
@TargetApi(26)
void createChannel() {
String channelName = getString(R.string.notify_grade_channel);
NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, channelName,
NotificationManager.IMPORTANCE_HIGH);
notificationChannel.enableLights(true);
notificationChannel.enableVibration(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
getManager().createNotificationChannel(notificationChannel);
}
@Override
String getChannelId() {
return CHANNEL_ID;
}
}

View File

@ -3,29 +3,22 @@ package io.github.wulkanowy.services;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import android.support.annotation.StringRes;
import android.support.v4.app.NotificationCompat;
import java.util.Random;
class NotificationService {
private static final String CHANNEL_ID = "Wulkanowy_New_Grade_Channel";
private static final String CHANNEL_NAME = "New Grade Channel";
public class NotificationService {
private NotificationManager manager;
private Context context;
NotificationService(Context context) {
public NotificationService(Context context) {
this.context = context;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannel();
}
}
void notify(Notification notification) {
@ -33,23 +26,32 @@ class NotificationService {
}
NotificationCompat.Builder notificationBuilder() {
return new NotificationCompat.Builder(context, CHANNEL_ID);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannel();
}
return new NotificationCompat.Builder(context, getChannelId());
}
@TargetApi(26)
private void createChannel() {
NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH);
notificationChannel.enableLights(true);
notificationChannel.enableVibration(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
getManager().createNotificationChannel(notificationChannel);
public void cancelAll() {
getManager().cancelAll();
}
private NotificationManager getManager() {
NotificationManager getManager() {
if (manager == null) {
manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
return manager;
}
}
String getString(@StringRes int stringId) {
return context.getString(stringId);
}
@TargetApi(26)
void createChannel() {
}
String getChannelId() {
return null;
}
}

View File

@ -4,6 +4,7 @@ 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;
@ -14,6 +15,7 @@ import com.firebase.jobdispatcher.RetryStrategy;
import com.firebase.jobdispatcher.SimpleJobService;
import com.firebase.jobdispatcher.Trigger;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
@ -27,32 +29,34 @@ import io.github.wulkanowy.utils.LogUtils;
public class SyncJob extends SimpleJobService {
private static final int DEFAULT_INTERVAL_START = 60 * 50;
private static final int DEFAULT_INTERVAL_END = DEFAULT_INTERVAL_START + (60 * 40);
public static final String EXTRA_INTENT_KEY = "cardId";
private List<Grade> gradeList;
public static final String JOB_TAG = "SyncJob";
private List<Grade> gradeList = new ArrayList<>();
@Inject
RepositoryContract repository;
public static void start(Context context) {
public static void start(Context context, int interval, boolean useOnlyWifi) {
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
dispatcher.mustSchedule(dispatcher.newJobBuilder()
.setLifetime(Lifetime.FOREVER)
.setService(SyncJob.class)
.setTag("SyncJob")
.setTag(JOB_TAG)
.setRecurring(true)
.setTrigger(Trigger.executionWindow(DEFAULT_INTERVAL_START, DEFAULT_INTERVAL_END))
.setConstraints(Constraint.ON_ANY_NETWORK)
.setTrigger(Trigger.executionWindow(interval * 60, (interval + 10) * 60))
.setConstraints(useOnlyWifi ? Constraint.ON_UNMETERED_NETWORK : Constraint.ON_ANY_NETWORK)
.setReplaceCurrent(false)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.build());
}
public static void stop(Context context) {
new FirebaseJobDispatcher(new GooglePlayDriver(context)).cancel(JOB_TAG);
}
@Override
public void onCreate() {
super.onCreate();
@ -62,28 +66,29 @@ public class SyncJob extends SimpleJobService {
@Override
public int onRunJob(JobParameters job) {
try {
repository.loginCurrentUser();
repository.initLastUser();
repository.syncAll();
gradeList = repository.getNewGrades();
if (!gradeList.isEmpty()) {
if (!gradeList.isEmpty() && repository.isNotifyEnable()) {
showNotification();
}
return JobService.RESULT_SUCCESS;
} catch (Exception e) {
Crashlytics.logException(e);
LogUtils.error("During background synchronization an error occurred", e);
return JobService.RESULT_FAIL_RETRY;
}
}
private void showNotification() {
NotificationService service = new NotificationService(getApplicationContext());
GradeNotify gradeNotify = new GradeNotify(getApplicationContext());
service.notify(service.notificationBuilder()
gradeNotify.notify(gradeNotify.notificationBuilder()
.setContentTitle(getStringTitle())
.setContentText(getStringContent())
.setSmallIcon(R.drawable.ic_stat_notify)
.setSmallIcon(R.drawable.ic_notify_grade)
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH)

View File

@ -25,6 +25,10 @@ public class BasePresenter<V extends BaseContract.View> implements BaseContract.
view = null;
}
protected boolean isViewAttached() {
return view != null;
}
public final RepositoryContract getRepository() {
return repository;
}

View File

@ -7,6 +7,7 @@ import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.ActionBar;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
@ -97,14 +98,12 @@ public class LoginActivity extends BaseActivity implements LoginContract.View {
@OnClick(R.id.login_activity_create_text)
void onCreateAccountButtonClick() {
CommonUtils.openInternalBrowserViewer(getApplicationContext(),
AppConstant.VULCAN_CREATE_ACCOUNT_URL);
CommonUtils.openInternalBrowserViewer(this, AppConstant.VULCAN_CREATE_ACCOUNT_URL);
}
@OnClick(R.id.login_activity_forgot_text)
void onForgotPasswordButtonClick() {
CommonUtils.openInternalBrowserViewer(getApplicationContext(),
AppConstant.VULCAN_FORGOT_PASS_URL);
CommonUtils.openInternalBrowserViewer(this, AppConstant.VULCAN_FORGOT_PASS_URL);
}
@Override
@ -211,6 +210,18 @@ public class LoginActivity extends BaseActivity implements LoginContract.View {
});
}
@Override
public void showActionBar(boolean show) {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
if (show) {
actionBar.show();
} else {
actionBar.hide();
}
}
}
@Override
public void onDestroy() {
super.onDestroy();

View File

@ -33,6 +33,8 @@ public interface LoginContract {
void hideSoftInput();
void showActionBar(boolean show);
}
@PerActivity
@ -42,6 +44,8 @@ public interface LoginContract {
void onStartAsync();
void onDoInBackground(int stepNumber) throws Exception;
void onLoginProgress(int step);
void onEndAsync(boolean success, Exception exception);

View File

@ -17,35 +17,32 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
private LoginTask loginAsync;
private String email;
private String password;
private String symbol;
@Inject
LoginPresenter(RepositoryContract repository) {
super(repository);
}
@Override
public void onDestroy() {
super.onDestroy();
if (loginAsync != null) {
loginAsync.cancel(true);
loginAsync = null;
}
}
@Override
public void attemptLogin(String email, String password, String symbol) {
getView().resetViewErrors();
this.email = email;
this.password = password;
this.symbol = getNormalizedSymbol(symbol);
if (!isAllFieldCorrect(password, email)) {
getView().showSoftInput();
return;
}
if (getView().isNetworkConnected()) {
// Dopóki używamy AsyncTask presenter będzie musiał "wiedzieć" o AsyncTaskach
loginAsync = new LoginTask(this,
email,
password,
getNormalizedSymbol(symbol));
loginAsync = new LoginTask(this);
loginAsync.execute();
} else {
@ -57,7 +54,22 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
@Override
public void onStartAsync() {
getView().showLoginProgress(true);
if (isViewAttached()) {
getView().showActionBar(false);
getView().showLoginProgress(true);
}
}
@Override
public void onDoInBackground(int stepNumber) throws Exception {
switch (stepNumber) {
case 1:
getRepository().registerUser(email, password, symbol);
break;
case 2:
getRepository().syncAll();
break;
}
}
@Override
@ -73,24 +85,27 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
public void onEndAsync(boolean success, Exception exception) {
if (success) {
getView().openMainActivity();
return;
} else if (exception instanceof BadCredentialsException) {
getView().setErrorPassIncorrect();
getView().showSoftInput();
getView().showLoginProgress(false);
} else if (exception instanceof AccountPermissionException) {
getView().setErrorSymbolRequired();
getView().showSoftInput();
getView().showLoginProgress(false);
} else {
getView().onError(getRepository().getErrorLoginMessage(exception));
getView().showLoginProgress(false);
}
getView().showActionBar(true);
getView().showLoginProgress(false);
}
@Override
public void onCanceledAsync() {
getView().showLoginProgress(false);
if (isViewAttached()) {
getView().showActionBar(true);
getView().showLoginProgress(false);
}
}
private boolean isEmailValid(String email) {
@ -140,4 +155,13 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
}
return correct;
}
@Override
public void onDestroy() {
if (loginAsync != null) {
loginAsync.cancel(true);
loginAsync = null;
}
super.onDestroy();
}
}

View File

@ -4,21 +4,12 @@ import android.os.AsyncTask;
public class LoginTask extends AsyncTask<Void, Integer, Boolean> {
private String email;
private String password;
private String symbol;
private LoginContract.Presenter presenter;
private Exception exception;
LoginTask(LoginContract.Presenter presenter, String email, String password, String symbol) {
LoginTask(LoginContract.Presenter presenter) {
this.presenter = presenter;
this.email = email;
this.password = password;
this.symbol = symbol;
}
@Override
@ -30,10 +21,10 @@ public class LoginTask extends AsyncTask<Void, Integer, Boolean> {
protected Boolean doInBackground(Void... params) {
try {
publishProgress(1);
presenter.getRepository().loginUser(email, password, symbol);
presenter.onDoInBackground(1);
publishProgress(2);
presenter.getRepository().syncAll();
presenter.onDoInBackground(2);
} catch (Exception e) {
exception = e;
return false;

View File

@ -18,15 +18,15 @@ import butterknife.ButterKnife;
import io.github.wulkanowy.R;
import io.github.wulkanowy.services.SyncJob;
import io.github.wulkanowy.ui.base.BaseActivity;
import io.github.wulkanowy.ui.main.attendance.AttendanceFragment;
import io.github.wulkanowy.ui.main.dashboard.DashboardFragment;
import io.github.wulkanowy.ui.main.grades.GradesFragment;
import io.github.wulkanowy.ui.main.settings.SettingsFragment;
import io.github.wulkanowy.ui.main.timetable.TimetableFragment;
public class MainActivity extends BaseActivity implements MainContract.View,
AHBottomNavigation.OnTabSelectedListener, OnFragmentIsReadyListener {
private int initTabPosition = 0;
@BindView(R.id.main_activity_nav)
AHBottomNavigation bottomNavigation;
@ -51,15 +51,10 @@ public class MainActivity extends BaseActivity implements MainContract.View,
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initTabPosition = getIntent().getIntExtra(SyncJob.EXTRA_INTENT_KEY, initTabPosition);
getActivityComponent().inject(this);
setButterKnife(ButterKnife.bind(this));
presenter.onStart(this);
initiationViewPager();
initiationBottomNav();
presenter.onStart(this, getIntent().getIntExtra(SyncJob.EXTRA_INTENT_KEY, -1));
}
@Override
@ -101,48 +96,44 @@ public class MainActivity extends BaseActivity implements MainContract.View,
presenter.onFragmentIsReady();
}
private void initiationBottomNav() {
bottomNavigation.addItem(new AHBottomNavigationItem(
getString(R.string.grades_text),
getResources().getDrawable(R.drawable.ic_menu_grade_26dp)
));
bottomNavigation.addItem(new AHBottomNavigationItem(
getString(R.string.attendance_text),
getResources().getDrawable(R.drawable.ic_menu_attendance_24dp)
));
bottomNavigation.addItem(new AHBottomNavigationItem(
getString(R.string.dashboard_text),
getResources().getDrawable(R.drawable.ic_menu_dashboard_24dp)
));
bottomNavigation.addItem(new AHBottomNavigationItem(
getString(R.string.lessonplan_text),
getResources().getDrawable(R.drawable.ic_menu_timetable_24dp)
));
bottomNavigation.addItem(new AHBottomNavigationItem(
getString(R.string.settings_text),
getResources().getDrawable(R.drawable.ic_menu_other_24dp)
));
@Override
public void initiationBottomNav(int tabPosition) {
bottomNavigation.addItem(new AHBottomNavigationItem(getString(R.string.grades_text),
R.drawable.ic_menu_grade_26dp));
bottomNavigation.addItem(new AHBottomNavigationItem(getString(R.string.attendance_text),
R.drawable.ic_menu_attendance_24dp));
bottomNavigation.addItem(new AHBottomNavigationItem(getString(R.string.dashboard_text),
R.drawable.ic_menu_dashboard_24dp));
bottomNavigation.addItem(new AHBottomNavigationItem(getString(R.string.timetable_text),
R.drawable.ic_menu_timetable_24dp));
bottomNavigation.addItem(new AHBottomNavigationItem(getString(R.string.settings_text),
R.drawable.ic_menu_other_24dp));
bottomNavigation.setAccentColor(getResources().getColor(R.color.colorPrimary));
bottomNavigation.setInactiveColor(Color.BLACK);
bottomNavigation.setBackgroundColor(getResources().getColor(R.color.colorBackgroundBottomNav));
bottomNavigation.setTitleState(AHBottomNavigation.TitleState.ALWAYS_SHOW);
bottomNavigation.setOnTabSelectedListener(this);
bottomNavigation.setCurrentItem(initTabPosition);
bottomNavigation.setCurrentItem(tabPosition);
bottomNavigation.setBehaviorTranslationEnabled(false);
}
private void initiationViewPager() {
@Override
public void initiationViewPager(int tabPosition) {
pagerAdapter.addFragment(new GradesFragment());
pagerAdapter.addFragment(new DashboardFragment());
pagerAdapter.addFragment(new AttendanceFragment());
pagerAdapter.addFragment(new DashboardFragment());
pagerAdapter.addFragment(new TimetableFragment());
pagerAdapter.addFragment(new DashboardFragment());
pagerAdapter.addFragment(new SettingsFragment());
viewPager.setPagingEnabled(false);
viewPager.setAdapter(pagerAdapter);
viewPager.setOffscreenPageLimit(4);
viewPager.setCurrentItem(initTabPosition, false);
viewPager.setCurrentItem(tabPosition, false);
}
@Override

View File

@ -14,11 +14,17 @@ public interface MainContract {
void showActionBar();
void hideActionBar();
void initiationViewPager(int tabPosition);
void initiationBottomNav(int tabPosition);
}
@PerActivity
interface Presenter extends BaseContract.Presenter<View> {
void onStart(View view, int tabPositionIntent);
void onTabSelected(int position, boolean wasSelected);
void onFragmentIsReady();

View File

@ -17,10 +17,21 @@ public class MainPresenter extends BasePresenter<MainContract.View>
}
@Override
public void onStart(MainContract.View view) {
public void onStart(MainContract.View view, int tabPositionIntent) {
super.onStart(view);
getView().showProgressBar(true);
getView().hideActionBar();
int tabPosition;
if (tabPositionIntent != -1) {
tabPosition = tabPositionIntent;
} else {
tabPosition = getRepository().getStartupTab();
}
getView().initiationBottomNav(tabPosition);
getView().initiationViewPager(tabPosition);
}
@Override
@ -32,11 +43,11 @@ public class MainPresenter extends BasePresenter<MainContract.View>
@Override
public void onFragmentIsReady() {
if (fragmentCount < 5) {
if (fragmentCount < 4) {
fragmentCount++;
}
if (fragmentCount == 5) {
if (fragmentCount == 4) {
getView().showActionBar();
getView().showProgressBar(false);
}

View File

@ -1,35 +1,35 @@
package io.github.wulkanowy.ui.main.timetable;
package io.github.wulkanowy.ui.main;
import android.support.v4.app.Fragment;
import java.util.ArrayList;
import java.util.List;
class TabsData {
public class TabsData {
private List<Fragment> fragments = new ArrayList<>();
private List<String> titles = new ArrayList<>();
Fragment getFragment(int index) {
public Fragment getFragment(int index) {
return fragments.get(index);
}
void addFragment(Fragment fragment) {
public void addFragment(Fragment fragment) {
if (fragment != null) {
fragments.add(fragment);
}
}
int getFragmentsCount() {
public int getFragmentsCount() {
return fragments.size();
}
String getTitle(int index) {
public String getTitle(int index) {
return titles.get(index);
}
void addTitle(String title) {
public void addTitle(String title) {
if (title != null) {
titles.add(title);
}

View File

@ -1,8 +1,9 @@
package io.github.wulkanowy.ui.main.attendance;
import io.github.wulkanowy.di.annotations.PerFragment;
import io.github.wulkanowy.di.annotations.PerActivity;
import io.github.wulkanowy.ui.base.BaseContract;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.TabsData;
public interface AttendanceContract {
@ -10,14 +11,22 @@ public interface AttendanceContract {
void setActivityTitle();
void scrollViewPagerToPosition(int position);
void setTabDataToAdapter(TabsData tabsData);
void setAdapterWithTabLayout();
boolean isMenuVisible();
}
@PerFragment
@PerActivity
interface Presenter extends BaseContract.Presenter<View> {
void onFragmentActivated(boolean isVisible);
void onStart(View view, OnFragmentIsReadyListener listener);
void onFragmentVisible(boolean isVisible);
void setRestoredPosition(int position);
}
}

View File

@ -0,0 +1,91 @@
package io.github.wulkanowy.ui.main.attendance;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
public class AttendanceDialogFragment extends DialogFragment {
private static final String ARGUMENT_KEY = "Item";
private AttendanceLesson lesson;
@BindView(R.id.attendance_dialog_subject_value)
TextView subject;
@BindView(R.id.attendance_dialog_date_value)
TextView date;
@BindView(R.id.attendance_dialog_number_value)
TextView number;
@BindView(R.id.attendance_dialog_description_value)
TextView description;
public AttendanceDialogFragment() {
//empty constructor for fragment
}
public static AttendanceDialogFragment newInstance(AttendanceLesson lesson) {
AttendanceDialogFragment dialogFragment = new AttendanceDialogFragment();
Bundle bundle = new Bundle();
bundle.putSerializable(ARGUMENT_KEY, lesson);
dialogFragment.setArguments(bundle);
return dialogFragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
lesson = (AttendanceLesson) getArguments().getSerializable(ARGUMENT_KEY);
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.attendance_dialog, container, false);
ButterKnife.bind(this, view);
if (!lesson.getSubject().isEmpty()) {
subject.setText(lesson.getSubject());
}
if (!lesson.getDate().isEmpty()) {
date.setText(lesson.getDate());
}
if (0 != lesson.getNumber()) {
number.setText(String.valueOf(lesson.getNumber()));
}
description.setText(lesson.getDescription());
if (lesson.getIsAbsenceUnexcused()) {
description.setTextColor(getResources().getColor(R.color.colorPrimaryDark));
}
return view;
}
@OnClick(R.id.attendance_dialog_close)
void onClickCloseButton() {
dismiss();
}
}

View File

@ -2,30 +2,44 @@ package io.github.wulkanowy.ui.main.attendance;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.github.wulkanowy.R;
import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.TabsData;
public class AttendanceFragment extends BaseFragment implements AttendanceContract.View {
private static final String CURRENT_ITEM_KEY = "CurrentItem";
@BindView(R.id.attendance_fragment_viewpager)
ViewPager viewPager;
@BindView(R.id.attendance_fragment_tab_layout)
TabLayout tabLayout;
@Inject
AttendancePagerAdapter pagerAdapter;
@Inject
AttendanceContract.Presenter presenter;
public AttendanceFragment() {
// empty constructor for fragment
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_attendance, container, false);
FragmentComponent component = getFragmentComponent();
@ -33,6 +47,10 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
presenter.onStart(this, (OnFragmentIsReadyListener) getActivity());
if (savedInstanceState != null) {
presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY));
}
}
return view;
@ -42,18 +60,48 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
if (presenter != null) {
presenter.onFragmentVisible(menuVisible);
presenter.onFragmentActivated(menuVisible);
}
}
@Override
public void setTabDataToAdapter(TabsData tabsData) {
pagerAdapter.setTabsData(tabsData);
}
@Override
public void setAdapterWithTabLayout() {
viewPager.setAdapter(pagerAdapter);
tabLayout.setupWithViewPager(viewPager);
}
@Override
public void scrollViewPagerToPosition(int position) {
viewPager.setCurrentItem(position, false);
}
@Override
public void setActivityTitle() {
setTitle(getString(R.string.dashboard_text));
setTitle(getString(R.string.attendance_text));
}
@Override
public void onError(String message) {
if (getActivity() != null) {
Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager),
message, Snackbar.LENGTH_LONG).show();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem());
super.onSaveInstanceState(outState);
}
@Override
public void onDestroyView() {
super.onDestroyView();
presenter.onDestroy();
super.onDestroyView();
}
}

View File

@ -0,0 +1,161 @@
package io.github.wulkanowy.ui.main.attendance;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.util.List;
import butterknife.BindColor;
import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem;
import eu.davidea.flexibleadapter.items.IFlexible;
import eu.davidea.viewholders.ExpandableViewHolder;
import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.Day;
public class AttendanceHeaderItem
extends AbstractExpandableHeaderItem<AttendanceHeaderItem.HeaderViewHolder, AttendanceSubItem> {
private Day day;
AttendanceHeaderItem(Day day) {
this.day = day;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AttendanceHeaderItem that = (AttendanceHeaderItem) o;
return new EqualsBuilder()
.append(day, that.day)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(day)
.toHashCode();
}
@Override
public int getLayoutRes() {
return R.layout.attendance_header;
}
@Override
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
return new HeaderViewHolder(view, adapter);
}
@Override
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, HeaderViewHolder holder,
int position, List<Object> payloads) {
holder.onBind(day, getSubItems());
}
static class HeaderViewHolder extends ExpandableViewHolder {
@BindView(R.id.attendance_header_day)
TextView dayName;
@BindView(R.id.attendance_header_date)
TextView date;
@BindView(R.id.attendance_header_description)
TextView description;
@BindView(R.id.attendance_header_alert_image)
ImageView alert;
@BindView(R.id.attendance_header_free_name)
TextView freeName;
@BindColor(R.color.secondary_text)
int secondaryColor;
@BindColor(R.color.free_day)
int backgroundFreeDay;
@BindColor(android.R.color.black)
int black;
private Context context;
HeaderViewHolder(View view, FlexibleAdapter adapter) {
super(view, adapter);
view.setOnClickListener(this);
ButterKnife.bind(this, view);
context = view.getContext();
}
void onBind(Day item, List<AttendanceSubItem> subItems) {
dayName.setText(StringUtils.capitalize(item.getDayName()));
date.setText(item.getDate());
int numberOfHours = countNotPresentHours(subItems);
description.setText((getContentView().getResources().getQuantityString(R.plurals.numberOfAbsences,
numberOfHours, numberOfHours)));
description.setVisibility(numberOfHours > 0 ? View.VISIBLE : View.INVISIBLE);
alert.setVisibility(isSubItemsHasChanges(subItems) ? View.VISIBLE : View.INVISIBLE);
freeName.setVisibility(subItems.isEmpty() ? View.VISIBLE : View.INVISIBLE);
setInactiveHeader(item.getAttendanceLessons().isEmpty());
}
private void setInactiveHeader(boolean inactive) {
((FrameLayout) getContentView()).setForeground(inactive ? null : getSelectableDrawable());
dayName.setTextColor(inactive ? secondaryColor : black);
if (inactive) {
getContentView().setBackgroundColor(backgroundFreeDay);
} else {
getContentView().setBackgroundDrawable(context.getResources()
.getDrawable(R.drawable.ic_border));
}
}
private Drawable getSelectableDrawable() {
int[] attrs = new int[]{R.attr.selectableItemBackground};
TypedArray typedArray = context.obtainStyledAttributes(attrs);
Drawable drawable = typedArray.getDrawable(0);
typedArray.recycle();
return drawable;
}
private int countNotPresentHours(List<AttendanceSubItem> subItems) {
int i = 0;
for (AttendanceSubItem subItem : subItems) {
if (subItem.getLesson().getIsAbsenceUnexcused()) {
i++;
}
}
return i;
}
private boolean isSubItemsHasChanges(List<AttendanceSubItem> subItems) {
for (AttendanceSubItem subItem : subItems) {
if (subItem.getLesson().getIsAbsenceUnexcused() || subItem.getLesson()
.getIsUnexcusedLateness()) {
return true;
}
}
return false;
}
}
}

View File

@ -0,0 +1,38 @@
package io.github.wulkanowy.ui.main.attendance;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import io.github.wulkanowy.ui.main.TabsData;
public class AttendancePagerAdapter extends FragmentStatePagerAdapter {
private TabsData tabsData;
public AttendancePagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
void setTabsData(TabsData tabsData) {
this.tabsData = tabsData;
}
@Override
public Fragment getItem(int position) {
return tabsData.getFragment(position);
}
@Override
public int getCount() {
return tabsData.getFragmentsCount();
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return tabsData.getTitle(position);
}
}

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