Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
6927ac0e4f | |||
2fd5b0f6ee | |||
b7a6b71a4d | |||
ce00c7025a | |||
5c558ae1f9 | |||
0708d84b98 | |||
b22d95392b | |||
78d57ca746 | |||
0aa8c5605d | |||
cb6afb137f | |||
c72e7748e2 | |||
5c9f10fa50 | |||
cdd3d6a53b | |||
8431661d54 | |||
3dabb11473 | |||
63b4ed42ca | |||
2bb2341d0f | |||
797e233809 | |||
64b964ca18 | |||
c9bdac6a66 |
@ -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
@ -32,6 +32,8 @@ local.properties
|
||||
.idea/tasks.xml
|
||||
.idea/vcs.xml
|
||||
.idea/workspace.xml
|
||||
.idea/caches/
|
||||
.idea/codeStyles/
|
||||
*.iml
|
||||
|
||||
# OS-specific files
|
||||
|
@ -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
|
||||
|
@ -4,9 +4,10 @@
|
||||
[](https://www.bitrise.io/app/daeff1893f3c8128)
|
||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||
[](https://bettercodehub.com/)
|
||||
[](https://scrutinizer-ci.com/g/wulkanowy/wulkanowy/?branch=master)
|
||||
[](https://snyk.io/test/github/wulkanowy/wulkanowy?targetFile=app%2Fbuild.gradle)
|
||||
[](https://bintray.com/wulkanowy/wulkanowy/api)
|
||||
|
||||
[Pobierz wersję rozwojową](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/app-debug-bitrise-signed.apk)
|
||||
|
||||
Wulkanowy to aplikacja na androida polepszająca wygodę używania dziennika UONET+.
|
||||
Androidowy klient dziennika VULCAN UONET+.
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -21,11 +21,9 @@ public class Client {
|
||||
|
||||
private String password;
|
||||
|
||||
private String symbol = "Default";
|
||||
private String symbol;
|
||||
|
||||
private Login login;
|
||||
|
||||
private Date lastSuccessRequest = new Date();
|
||||
private Date lastSuccessRequest = null;
|
||||
|
||||
private Cookies cookies = new Cookies();
|
||||
|
||||
@ -56,25 +54,16 @@ public class Client {
|
||||
return;
|
||||
}
|
||||
|
||||
this.symbol = getLogin().login(email, password, symbol);
|
||||
this.cookies = new Cookies();
|
||||
this.symbol = new Login(this).login(email, password, symbol);
|
||||
}
|
||||
|
||||
private boolean isLoggedIn() {
|
||||
return getCookies().size() > 0 &&
|
||||
return getCookies().size() > 0 && lastSuccessRequest != null &&
|
||||
29 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime());
|
||||
|
||||
}
|
||||
|
||||
Login getLogin() {
|
||||
if (null != login) {
|
||||
return login;
|
||||
}
|
||||
|
||||
login = new Login(this);
|
||||
|
||||
return login;
|
||||
}
|
||||
|
||||
public String getSymbol() {
|
||||
return symbol;
|
||||
}
|
||||
@ -83,6 +72,10 @@ public class Client {
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
public void addCookies(Map<String, String> items) {
|
||||
cookies.addItems(items);
|
||||
}
|
||||
|
||||
private Map<String, String> getCookies() {
|
||||
return cookies.getItems();
|
||||
}
|
||||
@ -98,8 +91,22 @@ public class Client {
|
||||
.replace("{symbol}", symbol);
|
||||
}
|
||||
|
||||
Document getPageByUrl(String url) throws IOException, VulcanException {
|
||||
login();
|
||||
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)
|
||||
@ -108,7 +115,13 @@ public class Client {
|
||||
|
||||
this.cookies.addItems(response.cookies());
|
||||
|
||||
return checkForErrors(response.parse());
|
||||
Document doc = checkForErrors(response.parse());
|
||||
|
||||
if (loginBefore) {
|
||||
lastSuccessRequest = new Date();
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
public Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException {
|
||||
@ -165,15 +178,21 @@ public class Client {
|
||||
}
|
||||
|
||||
Document checkForErrors(Document doc) throws VulcanException {
|
||||
if ("Przerwa techniczna".equals(doc.select("title").text())) {
|
||||
throw new VulcanOfflineException();
|
||||
lastSuccessRequest = null;
|
||||
|
||||
String title = doc.select("title").text();
|
||||
if ("Przerwa techniczna".equals(title)) {
|
||||
throw new VulcanOfflineException(title);
|
||||
}
|
||||
|
||||
if ("Zaloguj się".equals(doc.select(".loginButton").text())) {
|
||||
throw new NotLoggedInErrorException();
|
||||
String singIn = doc.select(".loginButton").text();
|
||||
if ("Zaloguj się".equals(singIn)) {
|
||||
throw new NotLoggedInErrorException(singIn);
|
||||
}
|
||||
|
||||
lastSuccessRequest = new Date();
|
||||
if ("Błąd strony".equals(title)) {
|
||||
throw new VulcanException("Nieznany błąd");
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
50
api/src/main/java/io/github/wulkanowy/api/Diary.java
Normal 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;
|
||||
}
|
||||
}
|
@ -1,4 +1,8 @@
|
||||
package io.github.wulkanowy.api;
|
||||
|
||||
public class NotLoggedInErrorException extends VulcanException {
|
||||
|
||||
public NotLoggedInErrorException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
14
api/src/main/java/io/github/wulkanowy/api/ParamItem.java
Normal 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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -8,17 +8,25 @@ import java.util.List;
|
||||
|
||||
public interface SnP {
|
||||
|
||||
String getId();
|
||||
String getSchoolID();
|
||||
|
||||
StudentAndParent storeContextCookies() throws IOException, VulcanException;
|
||||
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, VulcanException;
|
||||
|
||||
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);
|
||||
}
|
||||
|
46
api/src/main/java/io/github/wulkanowy/api/Student.java
Normal 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;
|
||||
}
|
||||
}
|
@ -5,8 +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 java.util.Map;
|
||||
|
||||
public class StudentAndParent implements SnP {
|
||||
|
||||
@ -18,28 +21,47 @@ public class StudentAndParent implements SnP {
|
||||
|
||||
private Client client;
|
||||
|
||||
private String id;
|
||||
private String schoolID;
|
||||
|
||||
StudentAndParent(Client client, String id) {
|
||||
private String studentID;
|
||||
|
||||
private String diaryID;
|
||||
|
||||
StudentAndParent(Client client, String schoolID, String studentID, String diaryID) {
|
||||
this.client = client;
|
||||
this.id = id;
|
||||
this.schoolID = schoolID;
|
||||
this.studentID = studentID;
|
||||
this.diaryID = diaryID;
|
||||
}
|
||||
|
||||
private String getBaseUrl() {
|
||||
return BASE_URL.replace("{ID}", getId());
|
||||
}
|
||||
public StudentAndParent setUp() throws IOException, VulcanException {
|
||||
if (null == getStudentID() || "".equals(getStudentID())) {
|
||||
Document doc = client.getPageByUrl(getSnpHomePageUrl());
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
Student student = getCurrent(getStudents(doc));
|
||||
studentID = student.getId();
|
||||
|
||||
Diary diary = getCurrent(getDiaries(doc));
|
||||
diaryID = diary.getId();
|
||||
}
|
||||
|
||||
public StudentAndParent storeContextCookies() throws IOException, VulcanException {
|
||||
client.getPageByUrl(getSnpHomePageUrl());
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getSchoolID() {
|
||||
return schoolID;
|
||||
}
|
||||
|
||||
public String getStudentID() {
|
||||
return studentID;
|
||||
}
|
||||
|
||||
private String getBaseUrl() {
|
||||
return BASE_URL.replace("{ID}", getSchoolID());
|
||||
}
|
||||
|
||||
String getSnpHomePageUrl() throws IOException, VulcanException {
|
||||
if (null != getId()) {
|
||||
if (null != getSchoolID()) {
|
||||
return getBaseUrl();
|
||||
}
|
||||
|
||||
@ -48,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;
|
||||
}
|
||||
@ -62,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];
|
||||
@ -72,8 +94,39 @@ public class StudentAndParent implements SnP {
|
||||
return e.select(".daneWiersz .wartosc").get(index - 1).text();
|
||||
}
|
||||
|
||||
public void setDiaryID(String id) {
|
||||
this.diaryID = id;
|
||||
}
|
||||
|
||||
public Document getSnPPageDocument(String url) throws IOException, VulcanException {
|
||||
return client.getPageByUrl(getBaseUrl() + url);
|
||||
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 {
|
||||
@ -88,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);
|
||||
}
|
||||
|
||||
@ -100,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"));
|
||||
}
|
||||
}
|
||||
|
@ -18,21 +18,27 @@ import io.github.wulkanowy.api.user.FamilyInformation;
|
||||
|
||||
public class Vulcan {
|
||||
|
||||
private String id;
|
||||
|
||||
private SnP snp;
|
||||
|
||||
private Client client;
|
||||
|
||||
public void setCredentials(String email, String password, String symbol, String id) {
|
||||
client = new Client(email, password, symbol);
|
||||
private String schoolId;
|
||||
|
||||
this.id = id;
|
||||
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 Client getClient() throws NotLoggedInErrorException {
|
||||
if (null == client) {
|
||||
throw new NotLoggedInErrorException();
|
||||
throw new NotLoggedInErrorException("Use setCredentials() method first");
|
||||
}
|
||||
|
||||
return client;
|
||||
@ -43,20 +49,17 @@ public class Vulcan {
|
||||
|
||||
}
|
||||
|
||||
public SnP getStudentAndParent() throws IOException, VulcanException {
|
||||
public SnP getStudentAndParent() throws VulcanException, IOException {
|
||||
if (null != this.snp) {
|
||||
return this.snp;
|
||||
}
|
||||
|
||||
this.snp = new StudentAndParent(getClient(), id).storeContextCookies();
|
||||
this.snp = new StudentAndParent(getClient(), schoolId, studentId, diaryId)
|
||||
.setUp();
|
||||
|
||||
return this.snp;
|
||||
}
|
||||
|
||||
public String getId() throws IOException, VulcanException {
|
||||
return getStudentAndParent().getId();
|
||||
}
|
||||
|
||||
public AttendanceTable getAttendanceTable() throws IOException, VulcanException {
|
||||
return new AttendanceTable(getStudentAndParent());
|
||||
}
|
||||
|
@ -1,4 +1,12 @@
|
||||
package io.github.wulkanowy.api;
|
||||
|
||||
public abstract class VulcanException extends Exception {
|
||||
public class VulcanException extends Exception {
|
||||
|
||||
protected VulcanException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
protected VulcanException(String message, Exception e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
package io.github.wulkanowy.api;
|
||||
|
||||
public class VulcanOfflineException extends VulcanException {
|
||||
|
||||
VulcanOfflineException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ 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<>();
|
||||
|
||||
@ -41,43 +41,61 @@ public class GradesList {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -3,4 +3,8 @@ package io.github.wulkanowy.api.login;
|
||||
import io.github.wulkanowy.api.VulcanException;
|
||||
|
||||
public class AccountPermissionException extends VulcanException {
|
||||
|
||||
AccountPermissionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
@ -3,4 +3,8 @@ package io.github.wulkanowy.api.login;
|
||||
import io.github.wulkanowy.api.VulcanException;
|
||||
|
||||
public class BadCredentialsException extends VulcanException {
|
||||
|
||||
BadCredentialsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ 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;
|
||||
|
||||
@ -17,56 +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 VulcanException, IOException {
|
||||
String certificate = sendCredentials(email, password, symbol);
|
||||
Document certDoc = sendCredentials(email, password);
|
||||
|
||||
return sendCertificate(certificate, symbol);
|
||||
return sendCertificate(certDoc, symbol);
|
||||
}
|
||||
|
||||
String sendCredentials(String email, String password, String symbol) throws IOException, VulcanException {
|
||||
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, VulcanException {
|
||||
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();
|
||||
|
||||
String title = client.postPageByUrl(LOGIN_ENDPOINT_PAGE_URL, new String[][]{
|
||||
{"wa", "wsignin1.0"},
|
||||
{"wresult", certificate}
|
||||
}).select("title").text();
|
||||
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();
|
||||
throw new AccountPermissionException("No account access. Try another symbol");
|
||||
}
|
||||
|
||||
if (!"Uonet+".equals(title)) {
|
||||
throw new LoginErrorException();
|
||||
throw new LoginErrorException("Expected page title `UONET+`, got " + title);
|
||||
}
|
||||
|
||||
return this.symbol;
|
||||
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", doc.select("input[name=wresult]").val()},
|
||||
{"wctx", doc.select("input[name=wctx]").val()}
|
||||
});
|
||||
}
|
||||
|
||||
private String findSymbol(String symbol, String certificate) {
|
||||
@ -78,14 +83,14 @@ public class Login {
|
||||
}
|
||||
|
||||
String findSymbolInCertificate(String certificate) {
|
||||
Elements els = Jsoup
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -2,5 +2,9 @@ package io.github.wulkanowy.api.login;
|
||||
|
||||
import io.github.wulkanowy.api.NotLoggedInErrorException;
|
||||
|
||||
public class LoginErrorException extends NotLoggedInErrorException {
|
||||
class LoginErrorException extends NotLoggedInErrorException {
|
||||
|
||||
LoginErrorException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
@ -3,4 +3,8 @@ package io.github.wulkanowy.api.messages;
|
||||
import io.github.wulkanowy.api.VulcanException;
|
||||
|
||||
class BadRequestException extends VulcanException {
|
||||
|
||||
BadRequestException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
@ -59,10 +59,10 @@ 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;
|
||||
@ -80,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;
|
||||
|
@ -1,13 +1,10 @@
|
||||
package io.github.wulkanowy.api;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import io.github.wulkanowy.api.login.Login;
|
||||
|
||||
public class ClientTest {
|
||||
|
||||
private String getFixtureAsString(String fixtureFileName) {
|
||||
@ -49,20 +46,6 @@ public class ClientTest {
|
||||
client.checkForErrors(doc);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClientTest() throws Exception {
|
||||
Client client = new Client("", "", "");
|
||||
|
||||
Assert.assertThat(client.getLogin(), CoreMatchers.instanceOf(Login.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClientTwiceTest() throws Exception {
|
||||
Client client = new Client("", "", "");
|
||||
|
||||
Assert.assertEquals(client.getLogin(), client.getLogin());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFilledUrlTest() throws Exception {
|
||||
Client client = new Client("http://fakelog.cf\\\\admin", "", "symbol123");
|
||||
|
@ -7,6 +7,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -22,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
|
||||
@ -43,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, null);
|
||||
StudentAndParent snp = new StudentAndParent(client, null, null, null);
|
||||
|
||||
Assert.assertEquals("https://uonetplus-opiekun.vulcan.net.pl/symbol/534213/Start/Index/",
|
||||
snp.getSnpHomePageUrl());
|
||||
@ -56,7 +60,7 @@ public class StudentAndParentTest {
|
||||
);
|
||||
|
||||
Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(wrongPageDocument);
|
||||
StudentAndParent snp = new StudentAndParent(client, null);
|
||||
StudentAndParent snp = new StudentAndParent(client, null, null, null);
|
||||
|
||||
snp.getSnpHomePageUrl();
|
||||
}
|
||||
@ -64,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/"));
|
||||
}
|
||||
@ -72,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/"));
|
||||
}
|
||||
@ -80,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());
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -16,7 +16,7 @@ public class VulcanTest {
|
||||
@Test
|
||||
public void getClientTest() throws Exception {
|
||||
Vulcan vulcan = new Vulcan();
|
||||
vulcan.setCredentials("email", "password", "symbol", null);
|
||||
vulcan.setCredentials("email", "password", "symbol", null, null, null);
|
||||
|
||||
Assert.assertThat(vulcan.getClient(), CoreMatchers.instanceOf(Client.class));
|
||||
}
|
||||
@ -24,7 +24,7 @@ public class VulcanTest {
|
||||
@Test
|
||||
public void getClientTwiceTest() throws Exception {
|
||||
Vulcan vulcan = new Vulcan();
|
||||
vulcan.setCredentials("email", "password", "symbol", null);
|
||||
vulcan.setCredentials("email", "password", "symbol", null, null, null);
|
||||
|
||||
Assert.assertEquals(vulcan.getClient(), vulcan.getClient());
|
||||
}
|
||||
|
@ -11,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);
|
||||
@ -26,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+","")
|
||||
);
|
||||
}
|
||||
|
||||
@ -53,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
|
||||
@ -61,21 +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");
|
||||
login.sendCertificate(getFixtureAsDocument("cert.xml"), "demo123");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -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>
|
||||
|
@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Witryna ucznia i rodzica – Strona głó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?&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?&id=1300">
|
||||
3Ti 2017
|
||||
</option>
|
||||
<option value="https://uonetplus-opiekun.fakelog.cf/Default/123456/Dziennik/DziennikOnChange?&id=1200">
|
||||
2Ti 2016
|
||||
</option>
|
||||
<option value="https://uonetplus-opiekun.fakelog.cf/Default/123456/Dziennik/DziennikOnChange?&id=1100">
|
||||
1Ti 2015
|
||||
</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<footer>wersja: 17.09.0008.26553</footer>
|
||||
</body>
|
||||
</html>
|
@ -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 3
|
||||
versionName "0.2.0"
|
||||
targetSdkVersion 26
|
||||
versionCode 7
|
||||
versionName "0.3.1"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
manifestPlaceholders = [
|
||||
@ -62,48 +62,45 @@ android {
|
||||
}
|
||||
|
||||
greendao {
|
||||
schemaVersion 22
|
||||
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.github.yuweiguocn:GreenDaoUpgradeHelper:v2.0.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"
|
||||
}
|
||||
|
@ -2,9 +2,6 @@ package io.github.wulkanowy.data.db.dao.entities;
|
||||
|
||||
import org.greenrobot.greendao.test.AbstractDaoTestLongPk;
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
|
||||
import io.github.wulkanowy.data.db.dao.entities.AttendanceLessonDao;
|
||||
|
||||
public class AttendanceLessonTest extends AbstractDaoTestLongPk<AttendanceLessonDao, AttendanceLesson> {
|
||||
|
||||
public AttendanceLessonTest() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -2,9 +2,6 @@ package io.github.wulkanowy.data.db.dao.entities;
|
||||
|
||||
import org.greenrobot.greendao.test.AbstractDaoTestLongPk;
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
|
||||
import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao;
|
||||
|
||||
public class TimetableLessonTest extends AbstractDaoTestLongPk<TimetableLessonDao, TimetableLesson> {
|
||||
|
||||
public TimetableLessonTest() {
|
||||
|
@ -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() {
|
||||
|
@ -68,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();
|
||||
|
@ -21,6 +21,16 @@ public interface RepositoryContract extends ResourcesContract, AccountSyncContra
|
||||
|
||||
long getCurrentUserId();
|
||||
|
||||
int getStartupTab();
|
||||
|
||||
boolean isServicesEnable();
|
||||
|
||||
boolean isNotifyEnable();
|
||||
|
||||
int getServicesInterval();
|
||||
|
||||
boolean isMobileDisable();
|
||||
|
||||
void syncGrades() throws VulcanException, IOException, ParseException;
|
||||
|
||||
void syncSubjects() throws VulcanException, IOException, ParseException;
|
||||
|
@ -3,19 +3,20 @@ package io.github.wulkanowy.data.db.dao;
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import com.github.yuweiguocn.library.greendao.MigrationHelper;
|
||||
|
||||
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.BuildConfig;
|
||||
import io.github.wulkanowy.data.db.dao.entities.AccountDao;
|
||||
import io.github.wulkanowy.api.Vulcan;
|
||||
import io.github.wulkanowy.data.db.dao.entities.DaoMaster;
|
||||
import io.github.wulkanowy.data.db.dao.entities.GradeDao;
|
||||
import io.github.wulkanowy.data.db.dao.entities.SubjectDao;
|
||||
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;
|
||||
@ -24,29 +25,16 @@ import io.github.wulkanowy.utils.LogUtils;
|
||||
@Singleton
|
||||
public class DbHelper extends DaoMaster.OpenHelper {
|
||||
|
||||
private SharedPrefContract sharedPref;
|
||||
private final SharedPrefContract sharedPref;
|
||||
|
||||
private final Vulcan vulcan;
|
||||
|
||||
@Inject
|
||||
DbHelper(@ApplicationContext Context context, @DatabaseInfo String dbName,
|
||||
SharedPrefContract sharedPref) {
|
||||
SharedPrefContract sharedPref, Vulcan vulcan) {
|
||||
super(context, dbName);
|
||||
this.sharedPref = sharedPref;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onUpgrade(Database db, int oldVersion, int newVersion) {
|
||||
MigrationHelper.DEBUG = BuildConfig.DEBUG;
|
||||
MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() {
|
||||
@Override
|
||||
public void onCreateAllTables(Database db, boolean ifNotExists) {
|
||||
DaoMaster.createAllTables(db, ifNotExists);
|
||||
}
|
||||
@Override
|
||||
public void onDropAllTables(Database db, boolean ifExists) {
|
||||
DaoMaster.dropAllTables(db, ifExists);
|
||||
}
|
||||
}, AccountDao.class, SubjectDao.class, GradeDao.class);
|
||||
this.vulcan = vulcan;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -55,7 +43,49 @@ public class DbHelper extends DaoMaster.OpenHelper {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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});
|
||||
}
|
||||
}
|
@ -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;
|
||||
@ -12,7 +14,6 @@ import javax.inject.Singleton;
|
||||
|
||||
import io.github.wulkanowy.R;
|
||||
import io.github.wulkanowy.api.NotLoggedInErrorException;
|
||||
import io.github.wulkanowy.api.VulcanOfflineException;
|
||||
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
|
||||
import io.github.wulkanowy.di.annotations.ApplicationContext;
|
||||
import io.github.wulkanowy.utils.AppConstant;
|
||||
@ -42,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);
|
||||
@ -51,8 +53,6 @@ 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();
|
||||
}
|
||||
@ -67,23 +67,23 @@ public class AppResources implements ResourcesContract {
|
||||
}
|
||||
|
||||
if (lesson.getIsAbsenceExcused()) {
|
||||
id = R.string.attendance_absence_excused;
|
||||
id = R.string.attendance_absence_excused;
|
||||
}
|
||||
|
||||
if (lesson.getIsAbsenceUnexcused()) {
|
||||
id = R.string.attendance_absence_unexcused;
|
||||
id = R.string.attendance_absence_unexcused;
|
||||
}
|
||||
|
||||
if (lesson.getIsExemption()) {
|
||||
id = R.string.attendance_exemption;
|
||||
id = R.string.attendance_exemption;
|
||||
}
|
||||
|
||||
if (lesson.getIsExcusedLateness()) {
|
||||
id = R.string.attendance_excused_lateness;
|
||||
id = R.string.attendance_excused_lateness;
|
||||
}
|
||||
|
||||
if (lesson.getIsUnexcusedLateness()) {
|
||||
id = R.string.attendance_unexcused_lateness;
|
||||
id = R.string.attendance_unexcused_lateness;
|
||||
}
|
||||
|
||||
return resources.getString(id);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -5,4 +5,14 @@ public interface SharedPrefContract {
|
||||
long getCurrentUserId();
|
||||
|
||||
void setCurrentUserId(long userId);
|
||||
|
||||
int getStartupTab();
|
||||
|
||||
int getServicesInterval();
|
||||
|
||||
boolean isMobileDisable();
|
||||
|
||||
boolean isServicesEnable();
|
||||
|
||||
boolean isNotifyEnable();
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ 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;
|
||||
@ -11,8 +12,11 @@ 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;
|
||||
@ -43,22 +47,27 @@ public class AccountSync implements AccountSyncContract {
|
||||
|
||||
LogUtils.debug("Register new user email=" + email);
|
||||
|
||||
vulcan.setCredentials(email, password, symbol, null);
|
||||
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())
|
||||
.setSnpId(vulcan.getStudentAndParent().getId());
|
||||
.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 VulcanException, IOException, CryptoException {
|
||||
public void initLastUser() throws IOException, CryptoException {
|
||||
|
||||
long userId = sharedPref.getCurrentUserId();
|
||||
|
||||
@ -73,6 +82,10 @@ public class AccountSync implements AccountSyncContract {
|
||||
vulcan.setCredentials(account.getEmail(),
|
||||
Scrambler.decrypt(account.getEmail(), account.getPassword()),
|
||||
account.getSymbol(),
|
||||
account.getSnpId());
|
||||
account.getSchoolId(),
|
||||
account.getRealId(),
|
||||
daoSession.getDiaryDao().queryBuilder()
|
||||
.where(DiaryDao.Properties.IsCurrent.eq(true)).unique().getValue()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
@ -67,23 +71,24 @@ public class SyncJob extends SimpleJobService {
|
||||
|
||||
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)
|
||||
|
@ -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;
|
||||
@ -209,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();
|
||||
|
@ -33,6 +33,8 @@ public interface LoginContract {
|
||||
|
||||
void hideSoftInput();
|
||||
|
||||
void showActionBar(boolean show);
|
||||
|
||||
}
|
||||
|
||||
@PerActivity
|
||||
|
@ -55,6 +55,7 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
|
||||
@Override
|
||||
public void onStartAsync() {
|
||||
if (isViewAttached()) {
|
||||
getView().showActionBar(false);
|
||||
getView().showLoginProgress(true);
|
||||
}
|
||||
}
|
||||
@ -84,24 +85,25 @@ 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() {
|
||||
if (isViewAttached()) {
|
||||
getView().showActionBar(true);
|
||||
getView().showLoginProgress(false);
|
||||
}
|
||||
}
|
||||
|
@ -21,13 +21,12 @@ 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;
|
||||
|
||||
@ -52,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
|
||||
@ -102,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 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
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -17,20 +17,16 @@ public interface AttendanceContract {
|
||||
|
||||
void setAdapterWithTabLayout();
|
||||
|
||||
void setChildFragmentSelected(int position, boolean selected);
|
||||
|
||||
boolean isMenuVisible();
|
||||
}
|
||||
|
||||
@PerActivity
|
||||
interface Presenter extends BaseContract.Presenter<View> {
|
||||
|
||||
void onFragmentVisible(boolean isVisible);
|
||||
|
||||
void onTabSelected(int position);
|
||||
|
||||
void onTabUnselected(int position);
|
||||
void onFragmentActivated(boolean isVisible);
|
||||
|
||||
void onStart(View view, OnFragmentIsReadyListener listener);
|
||||
|
||||
void setRestoredPosition(int position);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,9 @@ 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, TabLayout.OnTabSelectedListener {
|
||||
public class AttendanceFragment extends BaseFragment implements AttendanceContract.View {
|
||||
|
||||
private static final String CURRENT_ITEM_KEY = "CurrentItem";
|
||||
|
||||
@BindView(R.id.attendance_fragment_viewpager)
|
||||
ViewPager viewPager;
|
||||
@ -45,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;
|
||||
@ -54,25 +60,10 @@ 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 onTabSelected(TabLayout.Tab tab) {
|
||||
presenter.onTabSelected(tab.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {
|
||||
presenter.onTabUnselected(tab.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTabDataToAdapter(TabsData tabsData) {
|
||||
pagerAdapter.setTabsData(tabsData);
|
||||
@ -81,14 +72,7 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
|
||||
@Override
|
||||
public void setAdapterWithTabLayout() {
|
||||
viewPager.setAdapter(pagerAdapter);
|
||||
|
||||
tabLayout.setupWithViewPager(viewPager);
|
||||
tabLayout.addOnTabSelectedListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChildFragmentSelected(int position, boolean selected) {
|
||||
((AttendanceTabFragment) pagerAdapter.getItem(position)).setSelected(selected);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -109,6 +93,12 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem());
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
presenter.onDestroy();
|
||||
|
@ -1,5 +1,8 @@
|
||||
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;
|
||||
@ -16,6 +19,7 @@ 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;
|
||||
@ -55,12 +59,13 @@ public class AttendanceHeaderItem
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
|
||||
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
|
||||
return new HeaderViewHolder(view, adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List payloads) {
|
||||
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, HeaderViewHolder holder,
|
||||
int position, List<Object> payloads) {
|
||||
holder.onBind(day, getSubItems());
|
||||
}
|
||||
|
||||
@ -87,10 +92,16 @@ public class AttendanceHeaderItem
|
||||
@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) {
|
||||
@ -103,14 +114,30 @@ public class AttendanceHeaderItem
|
||||
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());
|
||||
}
|
||||
|
||||
if (item.getAttendanceLessons().isEmpty()) {
|
||||
((FrameLayout) getContentView()).setForeground(null);
|
||||
|
||||
private void setInactiveHeader(boolean inactive) {
|
||||
((FrameLayout) getContentView()).setForeground(inactive ? null : getSelectableDrawable());
|
||||
dayName.setTextColor(inactive ? secondaryColor : black);
|
||||
|
||||
if (inactive) {
|
||||
getContentView().setBackgroundColor(backgroundFreeDay);
|
||||
dayName.setTextColor(secondaryColor);
|
||||
} 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) {
|
||||
@ -118,17 +145,16 @@ public class AttendanceHeaderItem
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
private boolean isSubItemsHasChanges(List<AttendanceSubItem> subItems) {
|
||||
for (AttendanceSubItem subItem : subItems) {
|
||||
if (subItem.getLesson().getIsAbsenceUnexcused() || subItem.getLesson().getIsUnexcusedLateness()) {
|
||||
if (subItem.getLesson().getIsAbsenceUnexcused() || subItem.getLesson()
|
||||
.getIsUnexcusedLateness()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
|
||||
|
||||
private OnFragmentIsReadyListener listener;
|
||||
|
||||
private int positionToScroll;
|
||||
private int positionToScroll = 0;
|
||||
|
||||
private boolean isFirstSight = false;
|
||||
|
||||
@ -45,7 +45,10 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
|
||||
if (dates.isEmpty()) {
|
||||
dates = TimeUtils.getMondaysFromCurrentSchoolYear();
|
||||
}
|
||||
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
|
||||
|
||||
if (positionToScroll == 0) {
|
||||
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
|
||||
}
|
||||
|
||||
if (!isFirstSight) {
|
||||
isFirstSight = true;
|
||||
@ -57,22 +60,12 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFragmentVisible(boolean isVisible) {
|
||||
public void onFragmentActivated(boolean isVisible) {
|
||||
if (isVisible) {
|
||||
getView().setActivityTitle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelected(int position) {
|
||||
getView().setChildFragmentSelected(position, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(int position) {
|
||||
getView().setChildFragmentSelected(position, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDoInBackgroundLoading() throws Exception {
|
||||
for (String date : dates) {
|
||||
@ -97,6 +90,11 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRestoredPosition(int position) {
|
||||
this.positionToScroll = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
isFirstSight = false;
|
||||
|
@ -16,6 +16,7 @@ import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.davidea.flexibleadapter.items.AbstractSectionableItem;
|
||||
import eu.davidea.flexibleadapter.items.IFlexible;
|
||||
import eu.davidea.viewholders.FlexibleViewHolder;
|
||||
import io.github.wulkanowy.R;
|
||||
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
|
||||
@ -60,12 +61,13 @@ class AttendanceSubItem
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttendanceSubItem.SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
|
||||
return new AttendanceSubItem.SubItemViewHolder(view, adapter);
|
||||
public SubItemViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
|
||||
return new SubItemViewHolder(view, adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindViewHolder(FlexibleAdapter adapter, AttendanceSubItem.SubItemViewHolder holder, int position, List payloads) {
|
||||
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, SubItemViewHolder holder,
|
||||
int position, List<Object> payloads) {
|
||||
holder.onBind(lesson);
|
||||
}
|
||||
|
||||
|
@ -21,12 +21,10 @@ public interface AttendanceTabContract {
|
||||
|
||||
interface Presenter extends BaseContract.Presenter<AttendanceTabContract.View> {
|
||||
|
||||
void onFragmentSelected(boolean isSelected);
|
||||
void onFragmentActivated(boolean isSelected);
|
||||
|
||||
void setArgumentDate(String date);
|
||||
|
||||
void onStart(AttendanceTabContract.View view, boolean isPrimary);
|
||||
|
||||
void onRefresh();
|
||||
}
|
||||
}
|
||||
|
@ -27,12 +27,6 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
|
||||
|
||||
private static final String ARGUMENT_KEY = "date";
|
||||
|
||||
private static final String SAVED_KEY = "isSelected";
|
||||
|
||||
private boolean isPrimary = false;
|
||||
|
||||
private boolean isSelected = false;
|
||||
|
||||
@BindView(R.id.attendance_tab_fragment_recycler)
|
||||
RecyclerView recyclerView;
|
||||
|
||||
@ -51,6 +45,8 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
|
||||
@Inject
|
||||
FlexibleAdapter<AttendanceHeaderItem> adapter;
|
||||
|
||||
private boolean isFragmentVisible = false;
|
||||
|
||||
public static AttendanceTabFragment newInstance(String date) {
|
||||
AttendanceTabFragment fragmentTab = new AttendanceTabFragment();
|
||||
|
||||
@ -61,15 +57,6 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
|
||||
return fragmentTab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
isSelected = savedInstanceState.getBoolean(SAVED_KEY, isSelected);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
@ -84,7 +71,8 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
|
||||
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
|
||||
}
|
||||
|
||||
presenter.onStart(this, isPrimary);
|
||||
presenter.onStart(this);
|
||||
presenter.onFragmentActivated(isFragmentVisible);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
@ -111,10 +99,9 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
|
||||
@Override
|
||||
public void setMenuVisibility(boolean menuVisible) {
|
||||
super.setMenuVisibility(menuVisible);
|
||||
if (presenter != null && getView() != null) {
|
||||
presenter.onFragmentSelected(isSelected);
|
||||
} else if (isSelected) {
|
||||
isPrimary = true;
|
||||
isFragmentVisible = menuVisible;
|
||||
if (presenter != null) {
|
||||
presenter.onFragmentActivated(menuVisible);
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +112,7 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
|
||||
|
||||
@Override
|
||||
public void onRefreshSuccess() {
|
||||
onError(R.string.timetable_refresh_success);
|
||||
onError(R.string.sync_completed);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -143,10 +130,6 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
|
||||
noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
|
||||
public void setSelected(boolean selected) {
|
||||
isSelected = selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String message) {
|
||||
if (getActivity() != null) {
|
||||
@ -155,15 +138,8 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
outState.putBoolean(SAVED_KEY, isSelected);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
isPrimary = false;
|
||||
presenter.onDestroy();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
@ -33,21 +33,22 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(AttendanceTabContract.View view, boolean isPrimary) {
|
||||
public void onStart(AttendanceTabContract.View view) {
|
||||
super.onStart(view);
|
||||
getView().showProgressBar(true);
|
||||
getView().showNoItem(false);
|
||||
onFragmentSelected(isPrimary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFragmentSelected(boolean isSelected) {
|
||||
if (!isFirstSight && isSelected) {
|
||||
public void onFragmentActivated(boolean isSelected) {
|
||||
if (!isFirstSight && isSelected && isViewAttached()) {
|
||||
isFirstSight = true;
|
||||
|
||||
loadingTask = new AbstractTask();
|
||||
loadingTask.setOnFirstLoadingListener(this);
|
||||
loadingTask.execute();
|
||||
} else if (!isSelected) {
|
||||
cancelAsyncTasks();
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,6 +106,7 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
|
||||
boolean isEmptyWeek = true;
|
||||
|
||||
for (Day day : dayList) {
|
||||
day.resetAttendanceLessons();
|
||||
AttendanceHeaderItem headerItem = new AttendanceHeaderItem(day);
|
||||
|
||||
if (isEmptyWeek) {
|
||||
@ -156,10 +158,7 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
|
||||
getRepository().syncAttendance(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
isFirstSight = false;
|
||||
|
||||
private void cancelAsyncTasks() {
|
||||
if (refreshTask != null) {
|
||||
refreshTask.cancel(true);
|
||||
refreshTask = null;
|
||||
@ -168,7 +167,12 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
|
||||
loadingTask.cancel(true);
|
||||
loadingTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
cancelAsyncTasks();
|
||||
isFirstSight = false;
|
||||
super.onDestroy();
|
||||
}
|
||||
}
|
||||
|
@ -95,6 +95,16 @@ public class GradeHeaderItem
|
||||
? View.INVISIBLE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
super.onClick(view);
|
||||
if (subjectName.getLineCount() == 1) {
|
||||
subjectName.setMaxLines(3);
|
||||
} else {
|
||||
subjectName.setMaxLines(1);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSubItemsReadAndSaveAlertView(List<GradesSubItem> subItems) {
|
||||
boolean isRead = true;
|
||||
|
||||
|
@ -109,6 +109,7 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
|
||||
headerItems = new ArrayList<>();
|
||||
|
||||
for (Subject subject : subjectList) {
|
||||
subject.resetGradeList();
|
||||
List<Grade> gradeList = subject.getGradeList();
|
||||
|
||||
if (!gradeList.isEmpty()) {
|
||||
|
@ -37,7 +37,7 @@ public class GradesSubItem
|
||||
return grade;
|
||||
}
|
||||
|
||||
public void setSubjectAlertImage(View subjectAlertImage) {
|
||||
void setSubjectAlertImage(View subjectAlertImage) {
|
||||
this.subjectAlertImage = subjectAlertImage;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,70 @@
|
||||
package io.github.wulkanowy.ui.main.settings;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
|
||||
import io.github.wulkanowy.R;
|
||||
import io.github.wulkanowy.services.SyncJob;
|
||||
|
||||
public class SettingsFragment extends PreferenceFragmentCompat
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
public static final String SHARED_KEY_START_TAB = "startup_tab";
|
||||
|
||||
public static final String SHARED_KEY_SERVICES_ENABLE = "services_enable";
|
||||
|
||||
public static final String SHARED_KEY_NOTIFY_ENABLE = "notify_enable";
|
||||
|
||||
public static final String SHARED_KEY_SERVICES_INTERVAL = "services_interval";
|
||||
|
||||
public static final String SHARED_KEY_SERVICES_MOBILE_DISABLED = "services_disable_mobile";
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (key.equals(SHARED_KEY_SERVICES_ENABLE) || key.equals(SHARED_KEY_SERVICES_INTERVAL)
|
||||
|| key.equals(SHARED_KEY_SERVICES_MOBILE_DISABLED)) {
|
||||
launchServices(sharedPreferences.getBoolean(SHARED_KEY_SERVICES_ENABLE, true),
|
||||
sharedPreferences);
|
||||
}
|
||||
}
|
||||
|
||||
private void launchServices(boolean start, SharedPreferences sharedPref) {
|
||||
if (start) {
|
||||
int newInterval = Integer.parseInt(sharedPref.getString(SHARED_KEY_SERVICES_INTERVAL, "60"));
|
||||
boolean useOnlyWifi = sharedPref.getBoolean(SHARED_KEY_SERVICES_MOBILE_DISABLED, false);
|
||||
|
||||
SyncJob.stop(getContext());
|
||||
SyncJob.start(getContext(), newInterval, useOnlyWifi);
|
||||
} else {
|
||||
SyncJob.stop(getContext());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMenuVisibility(boolean menuVisible) {
|
||||
super.setMenuVisibility(menuVisible);
|
||||
if (menuVisible) {
|
||||
getActivity().setTitle(R.string.settings_text);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
getPreferenceScreen().getSharedPreferences()
|
||||
.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
getPreferenceScreen().getSharedPreferences()
|
||||
.unregisterOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
}
|
@ -17,20 +17,16 @@ public interface TimetableContract {
|
||||
|
||||
void setAdapterWithTabLayout();
|
||||
|
||||
void setChildFragmentSelected(int position, boolean selected);
|
||||
|
||||
boolean isMenuVisible();
|
||||
}
|
||||
|
||||
@PerActivity
|
||||
interface Presenter extends BaseContract.Presenter<View> {
|
||||
|
||||
void onFragmentVisible(boolean isVisible);
|
||||
|
||||
void onTabSelected(int position);
|
||||
|
||||
void onTabUnselected(int position);
|
||||
void onFragmentActivated(boolean isVisible);
|
||||
|
||||
void onStart(View view, OnFragmentIsReadyListener listener);
|
||||
|
||||
void setRestoredPosition(int position);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,9 @@ import io.github.wulkanowy.ui.base.BaseFragment;
|
||||
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
|
||||
import io.github.wulkanowy.ui.main.TabsData;
|
||||
|
||||
public class TimetableFragment extends BaseFragment implements TimetableContract.View, TabLayout.OnTabSelectedListener {
|
||||
public class TimetableFragment extends BaseFragment implements TimetableContract.View {
|
||||
|
||||
private static final String CURRENT_ITEM_KEY = "CurrentItem";
|
||||
|
||||
@BindView(R.id.timetable_fragment_viewpager)
|
||||
ViewPager viewPager;
|
||||
@ -44,6 +46,10 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
|
||||
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;
|
||||
}
|
||||
@ -52,25 +58,10 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
|
||||
public void setMenuVisibility(boolean menuVisible) {
|
||||
super.setMenuVisibility(menuVisible);
|
||||
if (presenter != null) {
|
||||
presenter.onFragmentVisible(menuVisible);
|
||||
presenter.onFragmentActivated(menuVisible);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
presenter.onTabSelected(tab.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {
|
||||
presenter.onTabUnselected(tab.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTabDataToAdapter(TabsData tabsData) {
|
||||
pagerAdapter.setTabsData(tabsData);
|
||||
@ -79,14 +70,7 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
|
||||
@Override
|
||||
public void setAdapterWithTabLayout() {
|
||||
viewPager.setAdapter(pagerAdapter);
|
||||
|
||||
tabLayout.setupWithViewPager(viewPager);
|
||||
tabLayout.addOnTabSelectedListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChildFragmentSelected(int position, boolean selected) {
|
||||
((TimetableTabFragment) pagerAdapter.getItem(position)).setSelected(selected);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -96,7 +80,7 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
|
||||
|
||||
@Override
|
||||
public void setActivityTitle() {
|
||||
setTitle(getString(R.string.lessonplan_text));
|
||||
setTitle(getString(R.string.timetable_text));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -107,6 +91,12 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem());
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
presenter.onDestroy();
|
||||
|
@ -1,5 +1,8 @@
|
||||
package io.github.wulkanowy.ui.main.timetable;
|
||||
|
||||
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;
|
||||
@ -16,6 +19,7 @@ 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;
|
||||
@ -55,12 +59,13 @@ public class TimetableHeaderItem
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
|
||||
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
|
||||
return new HeaderViewHolder(view, adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List payloads) {
|
||||
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, HeaderViewHolder holder,
|
||||
int position, List<Object> payloads) {
|
||||
holder.onBind(day, getSubItems());
|
||||
}
|
||||
|
||||
@ -84,10 +89,16 @@ public class TimetableHeaderItem
|
||||
@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<TimetableSubItem> subItems) {
|
||||
@ -96,14 +107,29 @@ public class TimetableHeaderItem
|
||||
alert.setVisibility(isSubItemNewMovedInOrChanged(subItems) ? View.VISIBLE : View.INVISIBLE);
|
||||
freeName.setVisibility(item.getIsFreeDay() ? View.VISIBLE : View.INVISIBLE);
|
||||
freeName.setText(item.getFreeDayName());
|
||||
setInactiveHeader(item.getIsFreeDay());
|
||||
}
|
||||
|
||||
if (item.getIsFreeDay()) {
|
||||
((FrameLayout) getContentView()).setForeground(null);
|
||||
private void setInactiveHeader(boolean inactive) {
|
||||
((FrameLayout) getContentView()).setForeground(inactive ? null : getSelectableDrawable());
|
||||
dayName.setTextColor(inactive ? secondaryColor : black);
|
||||
|
||||
if (inactive) {
|
||||
getContentView().setBackgroundColor(backgroundFreeDay);
|
||||
dayName.setTextColor(secondaryColor);
|
||||
} 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 boolean isSubItemNewMovedInOrChanged(List<TimetableSubItem> subItems) {
|
||||
boolean isAlertActive = false;
|
||||
|
||||
|
@ -24,7 +24,7 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
|
||||
|
||||
private OnFragmentIsReadyListener listener;
|
||||
|
||||
private int positionToScroll;
|
||||
private int positionToScroll = 0;
|
||||
|
||||
private boolean isFirstSight = false;
|
||||
|
||||
@ -45,7 +45,10 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
|
||||
if (dates.isEmpty()) {
|
||||
dates = TimeUtils.getMondaysFromCurrentSchoolYear();
|
||||
}
|
||||
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
|
||||
|
||||
if (positionToScroll == 0) {
|
||||
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
|
||||
}
|
||||
|
||||
if (!isFirstSight) {
|
||||
isFirstSight = true;
|
||||
@ -57,22 +60,12 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFragmentVisible(boolean isVisible) {
|
||||
public void onFragmentActivated(boolean isVisible) {
|
||||
if (isVisible) {
|
||||
getView().setActivityTitle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelected(int position) {
|
||||
getView().setChildFragmentSelected(position, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(int position) {
|
||||
getView().setChildFragmentSelected(position, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDoInBackgroundLoading() throws Exception {
|
||||
for (String date : dates) {
|
||||
@ -84,7 +77,6 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
|
||||
@Override
|
||||
public void onCanceledLoadingAsync() {
|
||||
//do nothing
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -97,6 +89,11 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRestoredPosition(int position) {
|
||||
this.positionToScroll = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
isFirstSight = false;
|
||||
|
@ -17,6 +17,7 @@ import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.davidea.flexibleadapter.items.AbstractSectionableItem;
|
||||
import eu.davidea.flexibleadapter.items.IFlexible;
|
||||
import eu.davidea.viewholders.FlexibleViewHolder;
|
||||
import io.github.wulkanowy.R;
|
||||
import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
|
||||
@ -27,7 +28,7 @@ public class TimetableSubItem
|
||||
|
||||
private TimetableLesson lesson;
|
||||
|
||||
public TimetableSubItem(TimetableHeaderItem header, TimetableLesson lesson) {
|
||||
TimetableSubItem(TimetableHeaderItem header, TimetableLesson lesson) {
|
||||
super(header);
|
||||
this.lesson = lesson;
|
||||
}
|
||||
@ -62,12 +63,13 @@ public class TimetableSubItem
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
|
||||
public SubItemViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
|
||||
return new SubItemViewHolder(view, adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindViewHolder(FlexibleAdapter adapter, SubItemViewHolder holder, int position, List payloads) {
|
||||
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, SubItemViewHolder holder,
|
||||
int position, List<Object> payloads) {
|
||||
holder.onBind(lesson);
|
||||
}
|
||||
|
||||
|
@ -23,12 +23,10 @@ public interface TimetableTabContract {
|
||||
|
||||
interface Presenter extends BaseContract.Presenter<View> {
|
||||
|
||||
void onFragmentSelected(boolean isSelected);
|
||||
void onFragmentActivated(boolean isSelected);
|
||||
|
||||
void setArgumentDate(String date);
|
||||
|
||||
void onStart(View view, boolean isPrimary);
|
||||
|
||||
void onRefresh();
|
||||
}
|
||||
}
|
||||
|
@ -28,12 +28,6 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
|
||||
|
||||
private static final String ARGUMENT_KEY = "date";
|
||||
|
||||
private static final String SAVED_KEY = "isSelected";
|
||||
|
||||
private boolean isPrimary = false;
|
||||
|
||||
private boolean isSelected = false;
|
||||
|
||||
@BindView(R.id.timetable_tab_fragment_recycler)
|
||||
RecyclerView recyclerView;
|
||||
|
||||
@ -55,6 +49,8 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
|
||||
@Inject
|
||||
FlexibleAdapter<TimetableHeaderItem> adapter;
|
||||
|
||||
private boolean isFragmentVisible = false;
|
||||
|
||||
public static TimetableTabFragment newInstance(String date) {
|
||||
TimetableTabFragment fragmentTab = new TimetableTabFragment();
|
||||
|
||||
@ -65,14 +61,6 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
|
||||
return fragmentTab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (savedInstanceState != null) {
|
||||
isSelected = savedInstanceState.getBoolean(SAVED_KEY, isSelected);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
@ -86,8 +74,8 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
|
||||
if (getArguments() != null) {
|
||||
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
|
||||
}
|
||||
|
||||
presenter.onStart(this, isPrimary);
|
||||
presenter.onStart(this);
|
||||
presenter.onFragmentActivated(isFragmentVisible);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
@ -114,10 +102,9 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
|
||||
@Override
|
||||
public void setMenuVisibility(boolean menuVisible) {
|
||||
super.setMenuVisibility(menuVisible);
|
||||
if (presenter != null && getView() != null) {
|
||||
presenter.onFragmentSelected(isSelected);
|
||||
} else if (isSelected) {
|
||||
isPrimary = true;
|
||||
isFragmentVisible = menuVisible;
|
||||
if (presenter != null) {
|
||||
presenter.onFragmentActivated(menuVisible);
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +120,7 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
|
||||
|
||||
@Override
|
||||
public void onRefreshSuccess() {
|
||||
onError(R.string.timetable_refresh_success);
|
||||
onError(R.string.sync_completed);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -151,10 +138,6 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
|
||||
noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
|
||||
public void setSelected(boolean selected) {
|
||||
isSelected = selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String message) {
|
||||
if (getActivity() != null) {
|
||||
@ -163,15 +146,8 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
outState.putBoolean(SAVED_KEY, isSelected);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
isPrimary = false;
|
||||
presenter.onDestroy();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
@ -36,21 +36,22 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(TimetableTabContract.View view, boolean isPrimary) {
|
||||
public void onStart(TimetableTabContract.View view) {
|
||||
super.onStart(view);
|
||||
getView().showProgressBar(true);
|
||||
getView().showNoItem(false);
|
||||
onFragmentSelected(isPrimary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFragmentSelected(boolean isSelected) {
|
||||
if (!isFirstSight && isSelected) {
|
||||
public void onFragmentActivated(boolean isSelected) {
|
||||
if (!isFirstSight && isSelected && isViewAttached()) {
|
||||
isFirstSight = true;
|
||||
|
||||
loadingTask = new AbstractTask();
|
||||
loadingTask.setOnFirstLoadingListener(this);
|
||||
loadingTask.execute();
|
||||
} else if (!isSelected) {
|
||||
cancelAsyncTasks();
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,6 +109,7 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
|
||||
boolean isFreeWeek = true;
|
||||
|
||||
for (Day day : dayList) {
|
||||
day.resetTimetableLessons();
|
||||
TimetableHeaderItem headerItem = new TimetableHeaderItem(day);
|
||||
|
||||
if (isFreeWeek) {
|
||||
@ -160,10 +162,7 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
|
||||
getRepository().syncTimetable(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
isFirstSight = false;
|
||||
|
||||
private void cancelAsyncTasks() {
|
||||
if (refreshTask != null) {
|
||||
refreshTask.cancel(true);
|
||||
refreshTask = null;
|
||||
@ -172,6 +171,12 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
|
||||
loadingTask.cancel(true);
|
||||
loadingTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
isFirstSight = false;
|
||||
cancelAsyncTasks();
|
||||
super.onDestroy();
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import android.os.Bundle;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import io.github.wulkanowy.services.NotificationService;
|
||||
import io.github.wulkanowy.services.SyncJob;
|
||||
import io.github.wulkanowy.ui.base.BaseActivity;
|
||||
import io.github.wulkanowy.ui.login.LoginActivity;
|
||||
@ -44,7 +45,12 @@ public class SplashActivity extends BaseActivity implements SplashContract.View
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSyncService() {
|
||||
SyncJob.start(getApplicationContext());
|
||||
public void startSyncService(int interval, boolean useOnlyWifi) {
|
||||
SyncJob.start(getApplicationContext(), interval, useOnlyWifi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelNotifications() {
|
||||
new NotificationService(getApplicationContext()).cancelAll();
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,9 @@ public interface SplashContract {
|
||||
|
||||
void openMainActivity();
|
||||
|
||||
void startSyncService();
|
||||
void startSyncService(int interval, boolean useOnlyWifi);
|
||||
|
||||
void cancelNotifications();
|
||||
}
|
||||
|
||||
@PerActivity
|
||||
|
@ -6,7 +6,6 @@ import javax.inject.Inject;
|
||||
|
||||
import io.github.wulkanowy.data.RepositoryContract;
|
||||
import io.github.wulkanowy.ui.base.BasePresenter;
|
||||
import io.github.wulkanowy.utils.LogUtils;
|
||||
|
||||
public class SplashPresenter extends BasePresenter<SplashContract.View>
|
||||
implements SplashContract.Presenter {
|
||||
@ -19,17 +18,17 @@ public class SplashPresenter extends BasePresenter<SplashContract.View>
|
||||
@Override
|
||||
public void onStart(@NonNull SplashContract.View activity) {
|
||||
super.onStart(activity);
|
||||
getView().startSyncService();
|
||||
getView().cancelNotifications();
|
||||
|
||||
if (getRepository().isServicesEnable()) {
|
||||
getView().startSyncService(getRepository().getServicesInterval(),
|
||||
getRepository().isMobileDisable());
|
||||
}
|
||||
|
||||
if (getRepository().getCurrentUserId() == 0) {
|
||||
getView().openLoginActivity();
|
||||
} else {
|
||||
try {
|
||||
getRepository().initLastUser();
|
||||
getView().openMainActivity();
|
||||
} catch (Exception e) {
|
||||
LogUtils.error("An error occurred when the application was started", e);
|
||||
}
|
||||
getView().openMainActivity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import java.util.List;
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
|
||||
import io.github.wulkanowy.data.db.dao.entities.Day;
|
||||
import io.github.wulkanowy.data.db.dao.entities.Diary;
|
||||
import io.github.wulkanowy.data.db.dao.entities.Grade;
|
||||
import io.github.wulkanowy.data.db.dao.entities.Subject;
|
||||
import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
|
||||
@ -17,8 +18,22 @@ public final class DataObjectConverter {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
public static List<Subject> subjectsToSubjectEntities(List<io.github.wulkanowy.api.grades.Subject> subjectList) {
|
||||
public static List<Diary> diariesToDiaryEntities(List<io.github.wulkanowy.api.Diary> diaryList) {
|
||||
List<Diary> diaryEntityList = new ArrayList<>();
|
||||
|
||||
for (io.github.wulkanowy.api.Diary diary : diaryList) {
|
||||
Diary diaryEntity = new Diary()
|
||||
.setValue(diary.getId())
|
||||
.setName(diary.getName())
|
||||
.setStudentId(diary.getStudentId())
|
||||
.setIsCurrent(diary.isCurrent());
|
||||
diaryEntityList.add(diaryEntity);
|
||||
}
|
||||
|
||||
return diaryEntityList;
|
||||
}
|
||||
|
||||
public static List<Subject> subjectsToSubjectEntities(List<io.github.wulkanowy.api.grades.Subject> subjectList) {
|
||||
List<Subject> subjectEntityList = new ArrayList<>();
|
||||
|
||||
for (io.github.wulkanowy.api.grades.Subject subject : subjectList) {
|
||||
@ -33,7 +48,6 @@ public final class DataObjectConverter {
|
||||
}
|
||||
|
||||
public static List<Grade> gradesToGradeEntities(List<io.github.wulkanowy.api.grades.Grade> gradeList) {
|
||||
|
||||
List<Grade> gradeEntityList = new ArrayList<>();
|
||||
|
||||
for (io.github.wulkanowy.api.grades.Grade grade : gradeList) {
|
||||
@ -67,7 +81,6 @@ public final class DataObjectConverter {
|
||||
|
||||
|
||||
public static List<Day> daysToDaysEntities(List<io.github.wulkanowy.api.generic.Day> dayList) {
|
||||
|
||||
List<Day> dayEntityList = new ArrayList<>();
|
||||
|
||||
for (io.github.wulkanowy.api.generic.Day day : dayList) {
|
||||
@ -110,7 +123,6 @@ public final class DataObjectConverter {
|
||||
}
|
||||
|
||||
public static List<TimetableLesson> lessonsToTimetableLessonsEntities(List<io.github.wulkanowy.api.generic.Lesson> lessonList) {
|
||||
|
||||
List<TimetableLesson> lessonEntityList = new ArrayList<>();
|
||||
|
||||
for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) {
|
||||
@ -120,7 +132,6 @@ public final class DataObjectConverter {
|
||||
}
|
||||
|
||||
public static List<AttendanceLesson> lessonsToAttendanceLessonsEntities(List<io.github.wulkanowy.api.generic.Lesson> lessonList) {
|
||||
|
||||
List<AttendanceLesson> lessonEntityList = new ArrayList<>();
|
||||
|
||||
for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
package io.github.wulkanowy.utils;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeConstants;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.threeten.bp.DayOfWeek;
|
||||
import org.threeten.bp.LocalDate;
|
||||
import org.threeten.bp.format.DateTimeFormatter;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
@ -13,14 +13,14 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static io.github.wulkanowy.utils.AppConstant.DATE_PATTERN;
|
||||
|
||||
public final class TimeUtils {
|
||||
|
||||
private static final long TICKS_AT_EPOCH = 621355968000000000L;
|
||||
|
||||
private static final long TICKS_PER_MILLISECOND = 10000;
|
||||
|
||||
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(AppConstant.DATE_PATTERN);
|
||||
|
||||
private TimeUtils() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
@ -33,7 +33,7 @@ public final class TimeUtils {
|
||||
}
|
||||
|
||||
public static long getNetTicks(String dateString) throws ParseException {
|
||||
return getNetTicks(dateString, DATE_PATTERN);
|
||||
return getNetTicks(dateString, AppConstant.DATE_PATTERN);
|
||||
}
|
||||
|
||||
public static long getNetTicks(String dateString, String dateFormat) throws ParseException {
|
||||
@ -49,12 +49,12 @@ public final class TimeUtils {
|
||||
}
|
||||
|
||||
public static List<String> getMondaysFromCurrentSchoolYear() {
|
||||
LocalDate startDate = new LocalDate(getCurrentSchoolYear(), 9, 1);
|
||||
LocalDate endDate = new LocalDate(getCurrentSchoolYear() + 1, 8, 31);
|
||||
LocalDate startDate = LocalDate.of(getCurrentSchoolYear(), 9, 1);
|
||||
LocalDate endDate = LocalDate.of(getCurrentSchoolYear() + 1, 8, 31);
|
||||
|
||||
List<String> dateList = new ArrayList<>();
|
||||
|
||||
LocalDate thisMonday = startDate.withDayOfWeek(DateTimeConstants.MONDAY);
|
||||
LocalDate thisMonday = startDate.with(DayOfWeek.MONDAY);
|
||||
|
||||
if (startDate.isAfter(thisMonday)) {
|
||||
startDate = thisMonday.plusWeeks(1);
|
||||
@ -63,27 +63,27 @@ public final class TimeUtils {
|
||||
}
|
||||
|
||||
while (startDate.isBefore(endDate)) {
|
||||
dateList.add(startDate.toString(DATE_PATTERN));
|
||||
dateList.add(startDate.format(formatter));
|
||||
startDate = startDate.plusWeeks(1);
|
||||
}
|
||||
return dateList;
|
||||
}
|
||||
|
||||
public static int getCurrentSchoolYear() {
|
||||
DateTime dateTime = new DateTime();
|
||||
return dateTime.getMonthOfYear() <= 8 ? dateTime.getYear() - 1 : dateTime.getYear();
|
||||
LocalDate localDate = LocalDate.now();
|
||||
return localDate.getMonthValue() <= 8 ? localDate.getYear() - 1 : localDate.getYear();
|
||||
}
|
||||
|
||||
public static String getDateOfCurrentMonday(boolean normalize) {
|
||||
DateTime currentDate = new DateTime();
|
||||
LocalDate currentDate = LocalDate.now();
|
||||
|
||||
if (currentDate.getDayOfWeek() == DateTimeConstants.SATURDAY && normalize) {
|
||||
if (currentDate.getDayOfWeek() == DayOfWeek.SATURDAY && normalize) {
|
||||
currentDate = currentDate.plusDays(2);
|
||||
} else if (currentDate.getDayOfWeek() == DateTimeConstants.SUNDAY && normalize) {
|
||||
} else if (currentDate.getDayOfWeek() == DayOfWeek.SUNDAY && normalize) {
|
||||
currentDate = currentDate.plusDays(1);
|
||||
} else {
|
||||
currentDate = currentDate.withDayOfWeek(DateTimeConstants.MONDAY);
|
||||
currentDate = currentDate.with(DayOfWeek.MONDAY);
|
||||
}
|
||||
return currentDate.toString(DATE_PATTERN);
|
||||
return currentDate.format(formatter);
|
||||
}
|
||||
}
|
||||
|
BIN
app/src/main/res/drawable-hdpi/ic_notify_grade.png
Normal file
After Width: | Height: | Size: 393 B |
Before Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/drawable-mdpi/ic_notify_grade.png
Normal file
After Width: | Height: | Size: 299 B |
Before Width: | Height: | Size: 730 B |
BIN
app/src/main/res/drawable-xhdpi/ic_notify_grade.png
Normal file
After Width: | Height: | Size: 469 B |
Before Width: | Height: | Size: 1.6 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_notify_grade.png
Normal file
After Width: | Height: | Size: 624 B |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.3 KiB |
12
app/src/main/res/values-pl/prefernces_array.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="services_interval_entries">
|
||||
<item>10 minut</item>
|
||||
<item>30 minut</item>
|
||||
<item>1 godzinę</item>
|
||||
<item>2 godziny</item>
|
||||
<item>6 godzin</item>
|
||||
<item>12 godzin</item>
|
||||
<item>24 godzin</item>
|
||||
</string-array>
|
||||
</resources>
|
@ -33,13 +33,12 @@
|
||||
<string name="dashboard_text">Dashboard</string>
|
||||
<string name="grades_text">Oceny</string>
|
||||
<string name="attendance_text">Frekwencja</string>
|
||||
<string name="lessonplan_text">Plan lekcji</string>
|
||||
<string name="timetable_text">Plan lekcji</string>
|
||||
<string name="settings_text">Ustawienia</string>
|
||||
<string name="activity_under_construction">Ta część aplikacji jest w budowie</string>
|
||||
<string name="fragment_no_grades">Brak ocen</string>
|
||||
|
||||
<string name="noInternet_text">Brak połączenia z internetem</string>
|
||||
<string name="error_host_offline">Przerwa techniczna. Spróbuj ponownie później</string>
|
||||
<string name="encrypt_failed_text">Szyfrowanie nie powiodło się. Automatyczne logowanie zostało wyłączone</string>
|
||||
<string name="version_text">Wersja %1$s</string>
|
||||
<string name="refresh_error_text">"Podczas odświeżania zawartości wystąpił błąd. "</string>
|
||||
@ -59,7 +58,7 @@
|
||||
<string name="timetable_dialog_time">Godziny</string>
|
||||
<string name="timetable_dialog_description">Zmiany</string>
|
||||
|
||||
<string name="timetable_refresh_success">Poprawnie zsynchronizowano</string>
|
||||
<string name="sync_completed">Synchronizacja zakończona</string>
|
||||
|
||||
<string name="generic_dialog_details">Szczegóły</string>
|
||||
<string name="generic_dialog_close">Zamknij</string>
|
||||
@ -114,4 +113,15 @@
|
||||
<item quantity="few">%1$d nieobecności</item>
|
||||
<item quantity="many">%1$d nieobecności</item>
|
||||
</plurals>
|
||||
|
||||
<string name="pref_view">Widok</string>
|
||||
<string name="pref_tab_list">Domyślny widok</string>
|
||||
<string name="pref_notify">Powiadomienia</string>
|
||||
<string name="pref_notify_switch">Pokazuj powiadomienia</string>
|
||||
<string name="pref_services">Usługi</string>
|
||||
<string name="pref_services_switch">Włącz odświeżanie danych w tle</string>
|
||||
<string name="pref_services_interval">Interwał między odświeżaniem danych</string>
|
||||
<string name="pref_services_mobile_data">Synchronizacja tylko przez WiFi</string>
|
||||
|
||||
<string name="notify_grade_channel">Nowe oceny</string>
|
||||
</resources>
|
||||
|
33
app/src/main/res/values/prefernces_array.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="startup_tab_entries" translatable="false">
|
||||
<item>@string/grades_text</item>
|
||||
<item>@string/attendance_text</item>
|
||||
<item>@string/dashboard_text</item>
|
||||
<item>@string/timetable_text</item>
|
||||
</string-array>
|
||||
<string-array name="startup_tab_value" translatable="false">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
</string-array>
|
||||
<string-array name="services_interval_entries">
|
||||
<item>10 minutes</item>
|
||||
<item>30 minutes</item>
|
||||
<item>1 hour</item>
|
||||
<item>2 hours</item>
|
||||
<item>6 hours</item>
|
||||
<item>12 hours</item>
|
||||
<item>24 hours</item>
|
||||
</string-array>
|
||||
<string-array name="services_interval_value" translatable="false">
|
||||
<item>10</item>
|
||||
<item>30</item>
|
||||
<item>60</item>
|
||||
<item>120</item>
|
||||
<item>360</item>
|
||||
<item>720</item>
|
||||
<item>1440</item>
|
||||
</string-array>
|
||||
</resources>
|
@ -33,7 +33,7 @@
|
||||
<string name="dashboard_text">Dashboard</string>
|
||||
<string name="grades_text">Grades</string>
|
||||
<string name="attendance_text">Attendance</string>
|
||||
<string name="lessonplan_text">Timetable</string>
|
||||
<string name="timetable_text">Timetable</string>
|
||||
<string name="settings_text">Settings</string>
|
||||
<string name="activity_under_construction">This section of app is under construction.</string>
|
||||
<string name="fragment_no_grades">No grades</string>
|
||||
@ -58,7 +58,7 @@
|
||||
<string name="timetable_dialog_time">Times</string>
|
||||
<string name="timetable_dialog_description">Changes</string>
|
||||
|
||||
<string name="timetable_refresh_success">Successful synchronisation</string>
|
||||
<string name="sync_completed">Synchronization completed</string>
|
||||
|
||||
<string name="generic_dialog_details">Details</string>
|
||||
<string name="generic_dialog_close">Close</string>
|
||||
@ -73,7 +73,6 @@
|
||||
<string name="info_average_grades">Average: %1$.2f</string>
|
||||
<string name="info_no_average">No average</string>
|
||||
<string name="info_free_week">No lesson in this week</string>
|
||||
<string name="error_host_offline">Technical break</string>
|
||||
|
||||
<string name="timetable_subitem_room">Room %s</string>
|
||||
|
||||
@ -110,4 +109,15 @@
|
||||
<item quantity="many">%1$d absences</item>
|
||||
<item quantity="other">%1$d absences</item>
|
||||
</plurals>
|
||||
|
||||
<string name="pref_tab_list">Default view after startup</string>
|
||||
<string name="pref_view">View</string>
|
||||
<string name="pref_notify">Notifications</string>
|
||||
<string name="pref_notify_switch">Show the notifications</string>
|
||||
<string name="pref_services">Services</string>
|
||||
<string name="pref_services_switch">Enable background data refreshing</string>
|
||||
<string name="pref_services_interval">Interval between data refreshing</string>
|
||||
<string name="pref_services_mobile_data">Synchronization via WiFi only</string>
|
||||
|
||||
<string name="notify_grade_channel">New grades</string>
|
||||
</resources>
|
||||
|
@ -13,6 +13,7 @@
|
||||
<item name="titleTextColor">@android:color/primary_text_dark</item>
|
||||
<item name="subtitleTextColor">@android:color/primary_text_dark</item>
|
||||
<item name="android:colorBackground">@android:color/white</item>
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
|
||||
</style>
|
||||
|
||||
<style name="WulkanowyTheme.SplashTheme" parent="WulkanowyTheme">
|
||||
|
38
app/src/main/res/xml/preferences.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceCategory android:title="@string/pref_view">
|
||||
<ListPreference
|
||||
android:defaultValue="2"
|
||||
android:entries="@array/startup_tab_entries"
|
||||
android:entryValues="@array/startup_tab_value"
|
||||
android:key="startup_tab"
|
||||
android:summary="%s"
|
||||
android:title="@string/pref_tab_list" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/pref_services">
|
||||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="services_enable"
|
||||
android:title="@string/pref_services_switch" />
|
||||
<ListPreference
|
||||
android:defaultValue="60"
|
||||
android:dependency="services_enable"
|
||||
android:entries="@array/services_interval_entries"
|
||||
android:entryValues="@array/services_interval_value"
|
||||
android:key="services_interval"
|
||||
android:summary="%s"
|
||||
android:title="@string/pref_services_interval" />
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="services_disable_mobile"
|
||||
android:title="@string/pref_services_mobile_data"
|
||||
android:dependency="services_enable" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/pref_notify">
|
||||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:dependency="services_enable"
|
||||
android:key="notify_enable"
|
||||
android:title="@string/pref_notify_switch" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
30
build.gradle
@ -6,10 +6,7 @@ buildscript {
|
||||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
classpath 'com.android.tools.build:gradle:3.1.0'
|
||||
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6.1"
|
||||
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
|
||||
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
|
||||
@ -29,6 +26,31 @@ subprojects {
|
||||
|
||||
ext {
|
||||
GROUP_ID = "io.github.wulkanowy"
|
||||
|
||||
supportVersion = "26.1.0"
|
||||
firebaseJob = "0.8.5"
|
||||
apacheLang = "3.7"
|
||||
apacheCollections = "4.1"
|
||||
flexibleAdapter = "5.0.2"
|
||||
flexibleUi = "1.0.0-b2"
|
||||
greenDao = "3.2.2"
|
||||
greenDaoHelper = "v2.0.2"
|
||||
butterknife = "8.8.1"
|
||||
threeTenABP = "1.0.5"
|
||||
dagger2 = "2.15"
|
||||
ahbottom = "2.1.0"
|
||||
jsoup = "1.10.3"
|
||||
gson = "2.8.2"
|
||||
|
||||
debugDb = "1.0.3"
|
||||
sqlcipher = "3.5.9"
|
||||
|
||||
junit = "4.12"
|
||||
mockito = "2.16.0"
|
||||
testRunner = "1.0.1"
|
||||
|
||||
crashlyticsSdk = "2.9.1"
|
||||
crashlyticsAnswers = "1.4.1"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
#Thu Oct 27 17:19:04 CEST 2017
|
||||
#Tue Mar 27 02:10:44 CEST 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
||||
|
@ -1,7 +1,11 @@
|
||||
apply plugin: "jacoco"
|
||||
|
||||
jacoco {
|
||||
toolVersion "0.7.4.201502262128"
|
||||
toolVersion "0.8.1"
|
||||
}
|
||||
|
||||
tasks.withType(Test) {
|
||||
jacoco.includeNoLocationClasses = true
|
||||
}
|
||||
|
||||
// run ./gradlew clean createDebugCoverageReport jacocoTestReport
|
||||
|