1
0

Compare commits

..

28 Commits
0.4.0 ... 0.4.5

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

View File

@ -199,24 +199,40 @@ jobs:
workflows:
version: 2
build_check_tests:
build-test-deploy:
jobs:
- build
- build:
filters:
tags:
only: /.*/
- lint:
filters:
tags:
only: /.*/
requires:
- build
- app-test:
filters:
tags:
only: /.*/
requires:
- build
- api-test:
filters:
tags:
only: /.*/
requires:
- build
- instrumented:
requires:
- build
filters:
tags:
only: /.*/
requires:
- build
- sonarcube:
filters:
tags:
only: /.*/
requires:
- build
- lint
@ -228,4 +244,6 @@ workflows:
- instrumented
filters:
tags:
only: /.*/
only: /\d+\.\d+\.\d+/
branches:
ignore: /.*/

View File

@ -4,9 +4,11 @@
[![Bitrise](https://img.shields.io/bitrise/daeff1893f3c8128/master.svg?token=Hjm1ACamk86JDeVVJHOeqQ&style=flat-square)](https://www.bitrise.io/app/daeff1893f3c8128)
[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy)
[![BCH compliance](https://bettercodehub.com/edge/badge/wulkanowy/wulkanowy?branch=master)](https://bettercodehub.com/)
[![Scrutinizer](https://img.shields.io/scrutinizer/g/wulkanowy/wulkanowy.svg)](https://scrutinizer-ci.com/g/wulkanowy/wulkanowy/?branch=master)
[![Known Vulnerabilities](https://snyk.io/test/github/wulkanowy/wulkanowy/badge.svg?targetFile=app%2Fbuild.gradle&style=flat-square)](https://snyk.io/test/github/wulkanowy/wulkanowy?targetFile=app%2Fbuild.gradle)
[![Scrutinizer](https://img.shields.io/scrutinizer/g/wulkanowy/wulkanowy.svg?style=flat-square)](https://scrutinizer-ci.com/g/wulkanowy/wulkanowy/?branch=master)
[![Bintray](https://img.shields.io/bintray/v/wulkanowy/wulkanowy/api.svg?style=flat-square)](https://bintray.com/wulkanowy/wulkanowy/api)
[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr)
[Pobierz wersję beta](https://play.google.com/store/apps/details?id=io.github.wulkanowy&amp;utm_source=vcs)
[Pobierz wersję rozwojową](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/app-debug-bitrise-signed.apk)

View File

@ -23,7 +23,7 @@ public class Client {
private String symbol;
private Date lastSuccessRequest = null;
private Date lastSuccessRequest;
private Cookies cookies = new Cookies();
@ -58,13 +58,12 @@ public class Client {
return;
}
this.cookies = new Cookies();
this.symbol = new Login(this).login(email, password, symbol);
}
private boolean isLoggedIn() {
return getCookies().size() > 0 && lastSuccessRequest != null &&
29 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime());
5 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime());
}
@ -103,7 +102,7 @@ public class Client {
return getPageByUrl(url, loginBefore, null);
}
public Document getPageByUrl(String url, boolean loginBefore, Map<String, String> cookies) throws IOException, VulcanException {
public synchronized Document getPageByUrl(String url, boolean loginBefore, Map<String, String> cookies) throws IOException, VulcanException {
if (loginBefore) {
login();
}
@ -119,7 +118,7 @@ public class Client {
this.cookies.addItems(response.cookies());
Document doc = checkForErrors(response.parse());
Document doc = checkForErrors(response.parse(), response.statusCode());
if (loginBefore) {
lastSuccessRequest = new Date();
@ -128,7 +127,7 @@ public class Client {
return doc;
}
public Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException {
public synchronized Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException {
Connection connection = Jsoup.connect(getFilledUrl(url));
for (String[] data : params) {
@ -143,7 +142,9 @@ public class Client {
this.cookies.addItems(response.cookies());
return checkForErrors(response.parse());
response.bufferUp(); // fixes cert parsing issues #109
return checkForErrors(response.parse(), response.statusCode());
}
public String getJsonStringByUrl(String url) throws IOException, VulcanException {
@ -181,7 +182,7 @@ public class Client {
return response.body();
}
Document checkForErrors(Document doc) throws VulcanException {
Document checkForErrors(Document doc, int code) throws VulcanException {
lastSuccessRequest = null;
String title = doc.select("title").text();
@ -195,7 +196,7 @@ public class Client {
}
if ("Błąd strony".equals(title)) {
throw new VulcanException("Nieznany błąd");
throw new NotLoggedInErrorException(title + " " + doc.selectFirst("p, body") + ", status: " + code);
}
return doc;

View File

@ -65,12 +65,12 @@ public class StudentAndParent implements SnP {
return getBaseUrl();
}
// get url to uonetplus-opiekun.vulcan.net.pl
// get url to uonetplus-opiekun.fakelog.cf
Document startPage = client.getPageByUrl(START_PAGE_URL);
Element studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a").first();
if (null == studentTileLink) {
throw new NotLoggedInErrorException("You are probably not logged in. Force login");
throw new VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?");
}
String snpPageUrl = studentTileLink.attr("href");
@ -84,7 +84,7 @@ public class StudentAndParent implements SnP {
String[] path = snpPageUrl.split(client.getHost())[1].split("/");
if (5 != path.length) {
throw new NotLoggedInErrorException("You are probably not logged in");
throw new NotLoggedInErrorException("You are probably not logged in " + snpPageUrl);
}
return path[2];
@ -106,7 +106,11 @@ public class StudentAndParent implements SnP {
Document doc = client.getPageByUrl(getBaseUrl() + url, true, cookies);
if ("Witryna ucznia i rodzica Strona główna".equals(doc.select("title").first().text())) {
if (!doc.title().startsWith("Witryna ucznia i rodzica")) {
throw new VulcanException("Expected SnP page, got page with title: " + doc.title());
}
if (doc.title().endsWith("Strona główna")) {
throw new VulcanException("Sesja została nieprawidłowo zainicjowana");
}

View File

@ -63,7 +63,7 @@ public class AttendanceTable {
for (int i = 1; i < size; i++) {
Lesson lesson = new Lesson();
lesson.setDate(days.get(i - 1).getDate());
lesson.setNumber(hours.get(0).text());
lesson.setNumber(Integer.valueOf(hours.get(0).text()));
addLessonDetails(lesson, hours.get(i));

View File

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

View File

@ -56,7 +56,7 @@ public class GradesList {
String descriptions = row.select("td:nth-child(3)").text();
String symbol = descriptions.split(", ")[0];
String description = descriptions.replaceFirst(symbol, "").replaceFirst(", ", "");
String description = descriptions.replaceFirst(Pattern.quote(symbol), "").replaceFirst(", ", "");
String color = getColor(row.select("td:nth-child(2) span.ocenaCzastkowa").attr("style"));
String date = formatDate(row.select("td:nth-child(5)").text());

View File

@ -9,11 +9,12 @@ import org.jsoup.select.Elements;
import java.io.IOException;
import io.github.wulkanowy.api.Client;
import io.github.wulkanowy.api.NotLoggedInErrorException;
import io.github.wulkanowy.api.VulcanException;
public class Login {
private static final String LOGIN_PAGE_URL = "{schema}://cufs.{host}/{symbol}/Account/LogOn" +
static final String LOGIN_PAGE_URL = "{schema}://cufs.{host}/{symbol}/Account/LogOn" +
"?ReturnUrl=%2F{symbol}%2FFS%2FLS%3Fwa%3Dwsignin1.0%26wtrealm%3D" +
"{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx%26wctx%3D" +
"{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx";
@ -27,6 +28,10 @@ public class Login {
public String login(String email, String password, String symbol) throws VulcanException, IOException {
Document certDoc = sendCredentials(email, password);
if ("Błąd".equals(certDoc.title())) {
throw new NotLoggedInErrorException(certDoc.selectFirst("body").text());
}
return sendCertificate(certDoc, symbol);
}
@ -36,27 +41,29 @@ public class Login {
{"Password", password}
};
String nextUrl = LOGIN_PAGE_URL;
Document loginPage = client.getPageByUrl(nextUrl, false);
Document nextDoc = sendCredentialsData(credentials, LOGIN_PAGE_URL);
Element formFirst = loginPage.select("#form1").first();
if (null != formFirst) { // on adfs login
Document formSecond = client.postPageByUrl(
formFirst.attr("abs:action"),
getFormStateParams(formFirst, "", "")
);
credentials = getFormStateParams(formSecond, email, password);
nextUrl = formSecond.select("#form1").first().attr("abs:action");
}
Document html = client.postPageByUrl(nextUrl, credentials);
Element errorMessage = html.select(".ErrorMessage, #ErrorTextLabel").first();
Element errorMessage = nextDoc.selectFirst(".ErrorMessage, #ErrorTextLabel");
if (null != errorMessage) {
throw new BadCredentialsException(errorMessage.text());
}
return html;
return nextDoc;
}
private Document sendCredentialsData(String[][] credentials, String nextUrl) throws IOException, VulcanException {
Element formFirst = client.getPageByUrl(nextUrl, false).selectFirst("#form1");
if (null != formFirst) { // only on adfs login
Document formSecond = client.postPageByUrl(
formFirst.attr("abs:action"),
getFormStateParams(formFirst, "", "")
);
credentials = getFormStateParams(formSecond, credentials[0][1], credentials[1][1]);
nextUrl = formSecond.selectFirst("#form1").attr("abs:action");
}
return client.postPageByUrl(nextUrl, credentials);
}
private String[][] getFormStateParams(Element form, String email, String password) {
@ -75,16 +82,13 @@ public class Login {
}
String sendCertificate(Document doc, String defaultSymbol) throws IOException, VulcanException {
String certificate = doc.select("input[name=wresult]").val();
String symbol = findSymbol(defaultSymbol, certificate);
client.setSymbol(symbol);
client.setSymbol(findSymbol(defaultSymbol, doc.select("input[name=wresult]").val()));
Document targetDoc = sendCertData(doc);
String title = targetDoc.select("title").text();
String title = targetDoc.title();
if ("Working...".equals(title)) { // on adfs login
title = sendCertData(targetDoc).select("title").text();
title = sendCertData(targetDoc).title();
}
if ("Logowanie".equals(title)) {
@ -95,7 +99,7 @@ public class Login {
throw new LoginErrorException("Expected page title `UONET+`, got " + title);
}
return symbol;
return client.getSymbol();
}
private Document sendCertData(Document doc) throws IOException, VulcanException {
@ -108,7 +112,7 @@ public class Login {
});
}
private String findSymbol(String symbol, String certificate) {
private String findSymbol(String symbol, String certificate) throws AccountPermissionException {
if ("Default".equals(symbol)) {
return findSymbolInCertificate(certificate);
}
@ -116,15 +120,19 @@ public class Login {
return symbol;
}
String findSymbolInCertificate(String certificate) {
String findSymbolInCertificate(String certificate) throws AccountPermissionException {
Elements instances = Jsoup
.parse(certificate.replaceAll(":", ""), "", Parser.xmlParser())
.select("[AttributeName=\"UserInstance\"] samlAttributeValue");
if (instances.isEmpty()) {
if (instances.isEmpty()) { // on adfs login
return "";
}
if (instances.size() < 2) { // 1st index is always `Default`
throw new AccountPermissionException("First login detected, specify symbol");
}
return instances.get(1).text();
}
}

View File

@ -19,7 +19,7 @@ import io.github.wulkanowy.api.generic.Week;
public class Timetable {
private static final String TIMETABLE_PAGE_URL = "Lekcja.mvc/PlanLekcji?data=";
private static final String TIMETABLE_PAGE_URL = "Lekcja.mvc/PlanZajec?data=";
private SnP snp;
@ -81,7 +81,7 @@ public class Timetable {
lesson.setStartTime(startEndEnd[0]);
lesson.setEndTime(startEndEnd[1]);
lesson.setDate(days.get(i - 2).getDate());
lesson.setNumber(hours.get(0).text());
lesson.setNumber(Integer.valueOf(hours.get(0).text()));
addLessonDetails(lesson, hours.get(i).select("div"));
@ -94,15 +94,21 @@ public class Timetable {
moveWarningToLessonNode(e);
switch (e.size()) {
case 2:
Element span = e.last().select("span").first();
if (span.hasClass(LessonTypes.CLASS_MOVED_OR_CANCELED)) {
lesson.setNewMovedInOrChanged(true);
lesson.setDescription("poprzednio: " + getLessonAndGroupInfoFromSpan(span)[0]);
}
case 1:
addLessonInfoFromElement(lesson, e.first());
break;
case 2:
Element span = e.last().selectFirst("span");
if (null == span) {
addLessonInfoFromElement(lesson, e.first());
} else if (span.hasClass(LessonTypes.CLASS_MOVED_OR_CANCELED)) {
lesson.setNewMovedInOrChanged(true);
lesson.setDescription("poprzednio: " + getLessonAndGroupInfoFromSpan(span)[0]);
addLessonInfoFromElement(lesson, e.first());
} else {
addLessonInfoFromElement(lesson, e.last());
}
break;
case 3:
addLessonInfoFromElement(lesson, e.get(1));
break;
@ -116,7 +122,9 @@ public class Timetable {
Elements warn = e.select(".uwaga-panel");
if (!warn.isEmpty()) {
e.select(".x-treelabel-rlz").last().text("(" + warn.text() + ")");
e.select("span").last()
.addClass("x-treelabel-rlz")
.text(warn.text());
e.remove(1);
}
}
@ -167,7 +175,8 @@ public class Timetable {
lesson.setRoom(spans.get(5).text());
lesson.setMovedOrCanceled(false);
lesson.setNewMovedInOrChanged(true);
lesson.setDescription(StringUtils.substringBetween(spans.last().text(), "(", ")")
lesson.setDescription(StringUtils.defaultString(StringUtils.substringBetween(
spans.last().text(), "(", ")"), spans.last().text())
+ " (poprzednio: " + spans.get(0).text() + ")");
} else if (9 == spans.size()) {
String[] subjectAndGroupInfo = getLessonAndGroupInfoFromSpan(spans.get(4));
@ -178,13 +187,15 @@ public class Timetable {
lesson.setMovedOrCanceled(false);
lesson.setNewMovedInOrChanged(true);
lesson.setDivisionIntoGroups(true);
lesson.setDescription(StringUtils.substringBetween(spans.last().text(), "(", ")")
lesson.setDescription(StringUtils.defaultString(StringUtils.substringBetween(
spans.last().text(), "(", ")"), spans.last().text())
+ " (poprzednio: " + getLessonAndGroupInfoFromSpan(spans.get(0))[0] + ")");
} else if (4 <= spans.size()) {
lesson.setSubject(spans.get(0).text());
lesson.setTeacher(spans.get(1).text());
lesson.setRoom(spans.get(2).text());
lesson.setDescription(StringUtils.substringBetween(spans.last().text(), "(", ")"));
lesson.setDescription(StringUtils.defaultString(StringUtils.substringBetween(
spans.last().text(), "(", ")"), spans.last().text()));
}
}
@ -212,7 +223,8 @@ public class Timetable {
return new String[]{
span.text().replace(" " + groupName, ""),
StringUtils.substringBetween(groupName, "[", "]")
StringUtils.defaultString(StringUtils.substringBetween(
groupName, "[", "]"), groupName)
};
}
}

View File

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

View File

@ -53,7 +53,7 @@ public class StudentAndParentTest {
snp.getSnpHomePageUrl());
}
@Test(expected = NotLoggedInErrorException.class)
@Test(expected = VulcanException.class)
public void getSnpPageUrlWithWrongPage() throws Exception {
Document wrongPageDocument = Jsoup.parse(
FixtureHelper.getAsString(getClass().getResourceAsStream("OcenyWszystkie-semester.html"))

View File

@ -19,7 +19,7 @@ public class GradesListTest extends StudentAndParentTestCase {
@Test
public void getAllTest() throws Exception {
Assert.assertEquals(6, filled.getAll().size()); // 2 items are skipped
Assert.assertEquals(7, filled.getAll().size()); // 2 items are skipped
}
@Test
@ -60,6 +60,7 @@ public class GradesListTest extends StudentAndParentTestCase {
Assert.assertEquals("BW3", list.get(3).getSymbol());
Assert.assertEquals("STR", list.get(4).getSymbol());
Assert.assertEquals("K", list.get(5).getSymbol());
Assert.assertEquals("+Odp", list.get(6).getSymbol());
}
@Test
@ -70,6 +71,7 @@ public class GradesListTest extends StudentAndParentTestCase {
Assert.assertEquals("Writing", list.get(3).getDescription());
Assert.assertEquals("", list.get(4).getDescription());
Assert.assertEquals("Kordian", list.get(5).getDescription());
Assert.assertEquals("Kordian", list.get(6).getDescription());
}
@Test

View File

@ -34,6 +34,10 @@ public class LoginTest {
Client client = getClient("Logowanie-success.html");
Mockito.when(client.getPageByUrl(Mockito.anyString(), Mockito.anyBoolean()))
.thenReturn(getFixtureAsDocument("Logowanie-error.html"));
Mockito.when(client.postPageByUrl(Mockito.eq(Login.LOGIN_PAGE_URL), Mockito.any(String[][].class)))
.thenReturn(getFixtureAsDocument("Logowanie-certyfikat.html"));
Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString());
Mockito.when(client.getSymbol()).thenCallRealMethod();
Login login = new Login(client);
Assert.assertEquals("d123", login.login("a@a", "pswd", "d123"));
@ -57,22 +61,31 @@ public class LoginTest {
Login login = new Login(client);
Assert.assertEquals(
getFixtureAsString("cert-stock.xml").replaceAll("\\s+",""),
login.sendCredentials("a@a", "passwd").select("input[name=wresult]").attr("value").replaceAll("\\s+","")
getFixtureAsString("cert-stock.xml").replaceAll("\\s+", ""),
login.sendCredentials("a@a", "passwd")
.select("input[name=wresult]")
.attr("value")
.replaceAll("\\s+", "")
);
}
@Test
public void sendCertificateNotDefaultSymbolSuccessTest() throws Exception {
Login login = new Login(getClient("Logowanie-success.html"));
Client client = getClient("Logowanie-success.html");
Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString());
Mockito.when(client.getSymbol()).thenCallRealMethod();
Login login = new Login(client);
Assert.assertEquals("wulkanowyschool321",
login.sendCertificate(new Document(""), "wulkanowyschool321"));
Assert.assertEquals("wulkanowyschool321", login.sendCertificate(
getFixtureAsDocument("Logowanie-certyfikat.html"), "wulkanowyschool321"));
}
@Test
public void sendCertificateDefaultSymbolSuccessTest() throws Exception {
Login login = new Login(getClient("Logowanie-success.html"));
Client client = getClient("Logowanie-success.html");
Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString());
Mockito.when(client.getSymbol()).thenCallRealMethod();
Login login = new Login(client);
Assert.assertEquals("demo12345",
login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "Default"));
@ -80,16 +93,18 @@ public class LoginTest {
@Test(expected = AccountPermissionException.class)
public void sendCertificateAccountPermissionTest() throws Exception {
Login login = new Login(getClient("Logowanie-brak-dostepu.html"));
Client client = getClient("Logowanie-brak-dostepu.html");
login.sendCertificate(getFixtureAsDocument("cert-stock.xml"), "demo123");
Login login = new Login(client);
login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "demo123");
}
@Test(expected = LoginErrorException.class)
public void sendCertificateLoginErrorTest() throws Exception {
Login login = new Login(getClient("Logowanie-certyfikat.html")); // change to other document
login.sendCertificate(getFixtureAsDocument("cert-stock.xml"), "demo123");
login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "demo123");
}
@Test
@ -101,10 +116,10 @@ public class LoginTest {
Assert.assertEquals("demo12345", login.findSymbolInCertificate(certificate));
}
@Test
public void findSymbolInInvalidCertificateTest() throws Exception {
@Test(expected = AccountPermissionException.class)
public void findSymbolInCertificateWithoutSecondInstanceTest() throws Exception {
Login login = new Login(getClient("Logowanie-certyfikat.html"));
Assert.assertEquals("", login.findSymbolInCertificate("<xml></xml>")); // change to real cert with empty symbols
login.findSymbolInCertificate(getFixtureAsString("cert-no-symbols.xml"));
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@ buildscript {
classpath "org.greenrobot:greendao-gradle-plugin:$greenDaoGradle"
classpath "io.fabric.tools:gradle:$fabricGradle"
classpath "com.google.gms:oss-licenses:0.9.2"
classpath 'com.github.triplet.gradle:play-publisher:1.2.0'
classpath "com.github.triplet.gradle:play-publisher:$playPublisher"
}
}
@ -41,8 +41,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 15
targetSdkVersion 26
versionCode 8
versionName "0.4.0"
versionCode 13
versionName "0.4.5"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
playAccountConfig = playAccountConfigs.defaultAccountConfig
@ -91,7 +91,7 @@ play {
}
greendao {
schemaVersion 26
schemaVersion 28
generateTests = true
}

View File

@ -1,8 +1,7 @@
package io.github.wulkanowy;
import android.app.Application;
import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.answers.Answers;
import com.crashlytics.android.core.CrashlyticsCore;
import com.jakewharton.threetenabp.AndroidThreeTen;
@ -10,18 +9,16 @@ import org.greenrobot.greendao.query.QueryBuilder;
import javax.inject.Inject;
import dagger.android.AndroidInjector;
import dagger.android.support.DaggerApplication;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.utils.Log;
import io.fabric.sdk.android.Fabric;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.di.component.ApplicationComponent;
import io.github.wulkanowy.di.component.DaggerApplicationComponent;
import io.github.wulkanowy.di.modules.ApplicationModule;
import io.github.wulkanowy.di.DaggerAppComponent;
import io.github.wulkanowy.utils.LogUtils;
public class WulkanowyApp extends Application {
protected ApplicationComponent applicationComponent;
public class WulkanowyApp extends DaggerApplication {
@Inject
RepositoryContract repository;
@ -31,12 +28,6 @@ public class WulkanowyApp extends Application {
super.onCreate();
AndroidThreeTen.init(this);
applicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(this))
.build();
applicationComponent.inject(this);
if (BuildConfig.DEBUG) {
enableDebugLog();
}
@ -61,14 +52,18 @@ public class WulkanowyApp extends Application {
private void initializeFabric() {
Fabric.with(new Fabric.Builder(this)
.kits(new Crashlytics.Builder()
.core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
.build())
.kits(
new Crashlytics.Builder()
.core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
.build(),
new Answers()
)
.debuggable(BuildConfig.DEBUG)
.build());
}
public ApplicationComponent getApplicationComponent() {
return applicationComponent;
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder().create(this);
}
}

View File

@ -4,6 +4,7 @@ import java.util.List;
import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.data.db.dao.entities.Symbol;
import io.github.wulkanowy.data.db.dao.entities.Week;
public interface DbContract {
@ -20,6 +21,8 @@ public interface DbContract {
long getCurrentSymbolId();
Symbol getCurrentSymbol();
long getCurrentDiaryId();
long getSemesterId(int name);

View File

@ -12,15 +12,16 @@ import java.util.Comparator;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.data.db.dao.entities.DaoMaster;
import io.github.wulkanowy.data.db.dao.migrations.Migration23;
import io.github.wulkanowy.data.db.dao.migrations.Migration26;
import io.github.wulkanowy.data.db.dao.migrations.Migration27;
import io.github.wulkanowy.data.db.dao.migrations.Migration28;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.di.annotations.DatabaseInfo;
import io.github.wulkanowy.utils.LogUtils;
@Singleton
@ -31,7 +32,7 @@ public class DbHelper extends DaoMaster.OpenHelper {
private final Vulcan vulcan;
@Inject
DbHelper(@ApplicationContext Context context, @DatabaseInfo String dbName,
DbHelper(Context context, @Named("dbName") String dbName,
SharedPrefContract sharedPref, Vulcan vulcan) {
super(context, dbName);
this.sharedPref = sharedPref;
@ -76,6 +77,8 @@ public class DbHelper extends DaoMaster.OpenHelper {
List<Migration> migrations = new ArrayList<>();
migrations.add(new Migration23());
migrations.add(new Migration26());
migrations.add(new Migration27());
migrations.add(new Migration28());
// Sorting just to be safe, in case other people add migrations in the wrong order.
Comparator<Migration> migrationComparator = new Comparator<Migration>() {

View File

@ -12,6 +12,7 @@ import io.github.wulkanowy.data.db.dao.entities.Semester;
import io.github.wulkanowy.data.db.dao.entities.SemesterDao;
import io.github.wulkanowy.data.db.dao.entities.StudentDao;
import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.data.db.dao.entities.Symbol;
import io.github.wulkanowy.data.db.dao.entities.SymbolDao;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao;
@ -57,10 +58,15 @@ public class DbRepository implements DbContract {
}
@Override
public long getCurrentSymbolId() {
public Symbol getCurrentSymbol() {
return daoSession.getSymbolDao().queryBuilder().where(
SymbolDao.Properties.UserId.eq(sharedPref.getCurrentUserId())
).unique().getId();
).unique();
}
@Override
public long getCurrentSymbolId() {
return getCurrentSymbol().getId();
}
@Override

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,7 +15,6 @@ import javax.inject.Singleton;
import io.github.wulkanowy.R;
import io.github.wulkanowy.api.NotLoggedInErrorException;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.utils.AppConstant;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.security.CryptoException;
@ -26,7 +25,7 @@ public class ResourcesRepository implements ResourcesContract {
private Resources resources;
@Inject
ResourcesRepository(@ApplicationContext Context context) {
ResourcesRepository(Context context) {
resources = context.getResources();
}
@ -52,7 +51,7 @@ public class ResourcesRepository implements ResourcesContract {
} else if (exception instanceof SocketTimeoutException) {
return resources.getString(R.string.generic_timeout_error);
} else if (exception instanceof NotLoggedInErrorException || exception instanceof IOException) {
return resources.getString(R.string.login_denied_text);
return resources.getString(R.string.login_failed_text);
} else {
return exception.getMessage();
}

View File

@ -6,10 +6,9 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.di.annotations.SharedPreferencesInfo;
import io.github.wulkanowy.ui.main.settings.SettingsFragment;
@Singleton
@ -24,7 +23,7 @@ public class SharedPrefRepository implements SharedPrefContract {
private final SharedPreferences settingsSharedPref;
@Inject
SharedPrefRepository(@ApplicationContext Context context, @SharedPreferencesInfo String sharedName) {
SharedPrefRepository(Context context, @Named("sharedPrefName") String sharedName) {
appSharedPref = context.getSharedPreferences(sharedName, Context.MODE_PRIVATE);
settingsSharedPref = PreferenceManager.getDefaultSharedPreferences(context);
}

View File

@ -2,6 +2,8 @@ package io.github.wulkanowy.data.sync;
import android.content.Context;
import org.greenrobot.greendao.database.Database;
import java.io.IOException;
import java.util.List;
@ -11,6 +13,7 @@ import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.VulcanException;
import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.DaoMaster;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Diary;
import io.github.wulkanowy.data.db.dao.entities.DiaryDao;
@ -20,7 +23,6 @@ import io.github.wulkanowy.data.db.dao.entities.StudentDao;
import io.github.wulkanowy.data.db.dao.entities.Symbol;
import io.github.wulkanowy.data.db.dao.entities.SymbolDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.security.CryptoException;
@ -39,7 +41,7 @@ public class AccountSync {
@Inject
AccountSync(DaoSession daoSession, SharedPrefContract sharedPref,
Vulcan vulcan, @ApplicationContext Context context) {
Vulcan vulcan, Context context) {
this.daoSession = daoSession;
this.sharedPref = sharedPref;
this.vulcan = vulcan;
@ -49,6 +51,8 @@ public class AccountSync {
public void registerUser(String email, String password, String symbol)
throws VulcanException, IOException, CryptoException {
clearUserData();
vulcan.setCredentials(email, password, symbol, null, null, null);
daoSession.getDatabase().beginTransaction();
@ -118,12 +122,12 @@ public class AccountSync {
daoSession.getSemesterDao().insertInTx(semesterList);
}
public void initLastUser() throws IOException, CryptoException {
public void initLastUser() throws CryptoException {
long userId = sharedPref.getCurrentUserId();
if (userId == 0) {
throw new IOException("Can't find saved user");
throw new NotRegisteredUserException("Can't find user id in SharedPreferences");
}
LogUtils.debug("Initialization current user id=" + userId);
@ -152,4 +156,11 @@ public class AccountSync {
diary.getValue()
);
}
private void clearUserData() {
Database database = daoSession.getDatabase();
DaoMaster.dropAllTables(database, true);
DaoMaster.createAllTables(database, true);
sharedPref.setCurrentUserId(0);
}
}

View File

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

View File

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

View File

@ -1,5 +1,7 @@
package io.github.wulkanowy.data.sync;
import org.apache.commons.collections4.CollectionUtils;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
@ -125,6 +127,16 @@ public class TimetableSync {
List<TimetableLesson> lessonsFromApiEntities = DataObjectConverter
.lessonsToTimetableLessonsEntities(lessons);
List<TimetableLesson> lessonsFromDbEntities = getLessonsFromDb(dayId);
if (!lessonsFromDbEntities.isEmpty()) {
List<TimetableLesson> lessonToRemove = new ArrayList<>(CollectionUtils.removeAll(lessonsFromDbEntities, lessonsFromApiEntities));
for (TimetableLesson timetableLesson : lessonToRemove) {
daoSession.getTimetableLessonDao().delete(timetableLesson);
}
}
for (TimetableLesson apiLessonEntity : lessonsFromApiEntities) {
TimetableLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId);
@ -148,4 +160,8 @@ public class TimetableSync {
TimetableLessonDao.Properties.EndTime.eq(apiEntity.getEndTime()))
.unique();
}
private List<TimetableLesson> getLessonsFromDb(long dayId) {
return daoSession.getDayDao().load(dayId).getTimetableLessons();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,10 +20,11 @@ import java.util.List;
import javax.inject.Inject;
import dagger.android.AndroidInjection;
import io.github.wulkanowy.R;
import io.github.wulkanowy.WulkanowyApp;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.sync.NotRegisteredUserException;
import io.github.wulkanowy.services.notifies.GradeNotify;
import io.github.wulkanowy.ui.main.MainActivity;
import io.github.wulkanowy.utils.LogUtils;
@ -59,7 +60,7 @@ public class SyncJob extends SimpleJobService {
@Override
public void onCreate() {
super.onCreate();
((WulkanowyApp) getApplication()).getApplicationComponent().inject(this);
AndroidInjection.inject(this);
}
@Override
@ -74,9 +75,12 @@ public class SyncJob extends SimpleJobService {
showNotification();
}
return JobService.RESULT_SUCCESS;
} catch (NotRegisteredUserException e) {
logError(e);
stop(getApplicationContext());
return JobService.RESULT_FAIL_NORETRY;
} catch (Exception e) {
Crashlytics.logException(e);
LogUtils.error("During background synchronization an error occurred", e);
logError(e);
return JobService.RESULT_FAIL_RETRY;
}
}
@ -116,4 +120,9 @@ public class SyncJob extends SimpleJobService {
gradeList.size(), gradeList.size());
}
}
private void logError(Exception e) {
Crashlytics.logException(e);
LogUtils.error("During background synchronization an error occurred", e);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,5 @@
package io.github.wulkanowy.ui.login;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.di.annotations.PerActivity;
import io.github.wulkanowy.ui.base.BaseContract;
public interface LoginContract {
@ -37,7 +35,6 @@ public interface LoginContract {
}
@PerActivity
interface Presenter extends BaseContract.Presenter<View> {
void attemptLogin(String email, String password, String symbol);
@ -51,7 +48,5 @@ public interface LoginContract {
void onEndAsync(boolean success, Exception exception);
void onCanceledAsync();
RepositoryContract getRepository();
}
}

View File

@ -0,0 +1,13 @@
package io.github.wulkanowy.ui.login;
import dagger.Binds;
import dagger.Module;
import io.github.wulkanowy.di.scopes.PerActivity;
@Module
public abstract class LoginModule {
@PerActivity
@Binds
abstract LoginContract.Presenter provideLoginPresenter(LoginPresenter loginPresenter);
}

View File

@ -11,6 +11,7 @@ import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.utils.AppConstant;
import io.github.wulkanowy.utils.FabricUtils;
public class LoginPresenter extends BasePresenter<LoginContract.View>
implements LoginContract.Presenter {
@ -46,7 +47,7 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
loginAsync.execute();
} else {
getView().onNoNetworkError();
getView().showNoNetworkMessage();
}
getView().hideSoftInput();
@ -84,6 +85,7 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
@Override
public void onEndAsync(boolean success, Exception exception) {
if (success) {
FabricUtils.logRegister(true, getRepository().getDbRepo().getCurrentSymbol().getSymbol());
getView().openMainActivity();
return;
} else if (exception instanceof BadCredentialsException) {
@ -93,7 +95,8 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
getView().setErrorSymbolRequired();
getView().showSoftInput();
} else {
getView().onError(getRepository().getResRepo().getErrorLoginMessage(exception));
FabricUtils.logRegister(false, symbol);
getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception));
}
getView().showActionBar(true);
@ -157,11 +160,11 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
}
@Override
public void onDestroy() {
public void detachView() {
if (loginAsync != null) {
loginAsync.cancel(true);
loginAsync = null;
}
super.onDestroy();
super.detachView();
}
}

View File

@ -4,6 +4,7 @@ import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.view.View;
@ -13,10 +14,11 @@ import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
import com.aurelhubert.ahbottomnavigation.AHBottomNavigationViewPager;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.github.wulkanowy.R;
import io.github.wulkanowy.services.jobs.SyncJob;
import io.github.wulkanowy.ui.base.BaseActivity;
import io.github.wulkanowy.ui.base.BasePagerAdapter;
import io.github.wulkanowy.ui.main.attendance.AttendanceFragment;
@ -39,6 +41,7 @@ public class MainActivity extends BaseActivity implements MainContract.View,
@BindView(R.id.main_activity_progress_bar)
View progressBar;
@Named("Main")
@Inject
BasePagerAdapter pagerAdapter;
@ -54,11 +57,9 @@ public class MainActivity extends BaseActivity implements MainContract.View,
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
injectViews();
getActivityComponent().inject(this);
setButterKnife(ButterKnife.bind(this));
presenter.onStart(this, getIntent().getIntExtra(EXTRA_CARD_ID_KEY, -1));
presenter.attachView(this, getIntent().getIntExtra(EXTRA_CARD_ID_KEY, -1));
}
@Override
@ -140,9 +141,20 @@ public class MainActivity extends BaseActivity implements MainContract.View,
viewPager.setCurrentItem(tabPosition, false);
}
@Override
public void startSyncService(int interval, boolean useOnlyWifi) {
SyncJob.start(getApplicationContext(), interval, useOnlyWifi);
}
@NonNull
@Override
protected View getMessageView() {
return findViewById(R.id.main_activity_view_pager);
}
@Override
protected void onDestroy() {
presenter.detachView();
super.onDestroy();
presenter.onDestroy();
}
}

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.main;
import io.github.wulkanowy.di.annotations.PerActivity;
import android.support.annotation.NonNull;
import io.github.wulkanowy.ui.base.BaseContract;
public interface MainContract {
@ -18,12 +19,13 @@ public interface MainContract {
void initiationViewPager(int tabPosition);
void initiationBottomNav(int tabPosition);
void startSyncService(int interval, boolean useOnlyWifi);
}
@PerActivity
interface Presenter extends BaseContract.Presenter<View> {
void onStart(View view, int tabPositionIntent);
void attachView(@NonNull View view, int initTabId);
void onTabSelected(int position, boolean wasSelected);

View File

@ -0,0 +1,50 @@
package io.github.wulkanowy.ui.main;
import javax.inject.Named;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.android.ContributesAndroidInjector;
import io.github.wulkanowy.di.scopes.PerActivity;
import io.github.wulkanowy.di.scopes.PerFragment;
import io.github.wulkanowy.ui.base.BasePagerAdapter;
import io.github.wulkanowy.ui.main.attendance.AttendanceFragment;
import io.github.wulkanowy.ui.main.attendance.AttendanceModule;
import io.github.wulkanowy.ui.main.exams.ExamsFragment;
import io.github.wulkanowy.ui.main.exams.ExamsModule;
import io.github.wulkanowy.ui.main.grades.GradesFragment;
import io.github.wulkanowy.ui.main.grades.GradesModule;
import io.github.wulkanowy.ui.main.timetable.TimetableFragment;
import io.github.wulkanowy.ui.main.timetable.TimetableModule;
@Module
public abstract class MainModule {
@PerActivity
@Binds
abstract MainContract.Presenter provideMainPresenter(MainPresenter mainPresenter);
@Named("Main")
@PerActivity
@Provides
static BasePagerAdapter provideAdapter(MainActivity activity) {
return new BasePagerAdapter(activity.getSupportFragmentManager());
}
@PerFragment
@ContributesAndroidInjector(modules = GradesModule.class)
abstract GradesFragment bindsGradesFragment();
@PerFragment
@ContributesAndroidInjector(modules = TimetableModule.class)
abstract TimetableFragment bindTimetableFragment();
@PerFragment
@ContributesAndroidInjector(modules = ExamsModule.class)
abstract ExamsFragment bindExamsFragment();
@PerFragment
@ContributesAndroidInjector(modules = AttendanceModule.class)
abstract AttendanceFragment bindAttendanceFramgnet();
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,32 @@
package io.github.wulkanowy.ui.main.attendance;
import javax.inject.Named;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.android.ContributesAndroidInjector;
import io.github.wulkanowy.di.scopes.PerChildFragment;
import io.github.wulkanowy.di.scopes.PerFragment;
import io.github.wulkanowy.ui.base.BasePagerAdapter;
import io.github.wulkanowy.ui.main.attendance.tab.AttendanceTabFragment;
import io.github.wulkanowy.ui.main.attendance.tab.AttendanceTabModule;
@Module
public abstract class AttendanceModule {
@PerFragment
@Binds
abstract AttendanceContract.Presenter provideAttendancePresenter(AttendancePresenter attendancePresenter);
@PerFragment
@Named("Attendance")
@Provides
static BasePagerAdapter providePagerAdapter(AttendanceFragment fragment) {
return new BasePagerAdapter(fragment.getChildFragmentManager());
}
@PerChildFragment
@ContributesAndroidInjector(modules = AttendanceTabModule.class)
abstract AttendanceTabFragment bindAttendanceTabFragment();
}

View File

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

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.main.attendance;
package io.github.wulkanowy.ui.main.attendance.tab;
import android.content.Context;
import android.content.res.TypedArray;

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.main.attendance;
package io.github.wulkanowy.ui.main.attendance.tab;
import android.content.Context;
import android.support.v4.app.DialogFragment;
@ -20,6 +20,7 @@ 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;
import io.github.wulkanowy.ui.main.attendance.AttendanceDialogFragment;
class AttendanceSubItem
extends AbstractSectionableItem<AttendanceSubItem.SubItemViewHolder, AttendanceHeaderItem> {

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.main.attendance;
package io.github.wulkanowy.ui.main.attendance.tab;
import java.util.List;

View File

@ -1,9 +1,8 @@
package io.github.wulkanowy.ui.main.attendance;
package io.github.wulkanowy.ui.main.attendance.tab;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@ -15,11 +14,9 @@ import java.util.List;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
import io.github.wulkanowy.R;
import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.ui.base.BaseFragment;
public class AttendanceTabFragment extends BaseFragment implements AttendanceTabContract.View,
@ -61,34 +58,28 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_attendance_tab, container, false);
injectViews(view);
FragmentComponent component = getFragmentComponent();
if (component != null) {
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
if (getArguments() != null) {
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
}
presenter.onStart(this);
presenter.onFragmentActivated(isFragmentVisible);
if (getArguments() != null) {
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
}
presenter.attachView(this);
presenter.onFragmentActivated(isFragmentVisible);
return view;
}
@Override
protected void setUpOnViewCreated(View fragmentView) {
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
adapter.setAutoCollapseOnExpand(true);
adapter.setAutoScrollOnExpand(true);
adapter.expandItemsAtStartUp();
recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext()));
recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext()));
recyclerView.setAdapter(adapter);
refreshLayout.setColorSchemeResources(android.R.color.black);
refreshLayout.setOnRefreshListener(this);
}
@Override
@ -112,7 +103,7 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
@Override
public void onRefreshSuccess() {
onError(R.string.sync_completed);
showMessage(R.string.sync_completed);
}
@Override
@ -130,17 +121,9 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}
@Override
public void onError(String message) {
if (getActivity() != null) {
Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager),
message, Snackbar.LENGTH_LONG).show();
}
}
@Override
public void onDestroyView() {
presenter.onDestroy();
presenter.detachView();
super.onDestroyView();
}
}

View File

@ -0,0 +1,21 @@
package io.github.wulkanowy.ui.main.attendance.tab;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import io.github.wulkanowy.di.scopes.PerChildFragment;
@Module
public abstract class AttendanceTabModule {
@PerChildFragment
@Binds
abstract AttendanceTabContract.Presenter provideAttendanceTabPresenter(AttendanceTabPresenter attendanceTabPresenter);
@PerChildFragment
@Provides
static FlexibleAdapter<AttendanceHeaderItem> provideAdapter() {
return new FlexibleAdapter<>(null);
}
}

View File

@ -1,4 +1,6 @@
package io.github.wulkanowy.ui.main.attendance;
package io.github.wulkanowy.ui.main.attendance.tab;
import android.support.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
@ -10,6 +12,7 @@ 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.Week;
import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.utils.FabricUtils;
import io.github.wulkanowy.utils.async.AbstractTask;
import io.github.wulkanowy.utils.async.AsyncListeners;
@ -33,8 +36,9 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
}
@Override
public void onStart(AttendanceTabContract.View view) {
super.onStart(view);
public void attachView(@NonNull AttendanceTabContract.View view) {
super.attachView(view);
getView().showProgressBar(true);
getView().showNoItem(false);
}
@ -59,7 +63,7 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
refreshTask.setOnRefreshListener(this);
refreshTask.execute();
} else {
getView().onNoNetworkError();
getView().showNoNetworkMessage();
getView().hideRefreshingBar();
}
}
@ -85,9 +89,11 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
getView().onRefreshSuccess();
} else {
getView().onError(getRepository().getResRepo().getErrorLoginMessage(exception));
getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception));
}
getView().hideRefreshingBar();
FabricUtils.logRefresh("Attendance", result, date);
}
@Override
@ -180,9 +186,9 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
}
@Override
public void onDestroy() {
public void detachView() {
cancelAsyncTasks();
isFirstSight = false;
super.onDestroy();
super.detachView();
}
}

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.main.exams;
import io.github.wulkanowy.di.annotations.PerActivity;
import android.support.annotation.NonNull;
import io.github.wulkanowy.ui.base.BaseContract;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
@ -21,10 +22,9 @@ public interface ExamsContract {
void setThemeForTab(int position);
}
@PerActivity
interface Presenter extends BaseContract.Presenter<View> {
void onStart(View view, OnFragmentIsReadyListener listener);
void attachView(@NonNull View view, OnFragmentIsReadyListener listener);
void onFragmentActivated(boolean isVisible);

View File

@ -10,14 +10,15 @@ import android.view.View;
import android.view.ViewGroup;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.github.wulkanowy.R;
import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.base.BasePagerAdapter;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.exams.tab.ExamsTabFragment;
public class ExamsFragment extends BaseFragment implements ExamsContract.View {
@ -30,6 +31,7 @@ public class ExamsFragment extends BaseFragment implements ExamsContract.View {
TabLayout tabLayout;
@Inject
@Named("Exams")
BasePagerAdapter pagerAdapter;
@Inject
@ -39,16 +41,12 @@ public class ExamsFragment extends BaseFragment implements ExamsContract.View {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_exams, container, false);
injectViews(view);
FragmentComponent component = getFragmentComponent();
if (component != null) {
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
presenter.onStart(this, (OnFragmentIsReadyListener) getActivity());
presenter.attachView(this, (OnFragmentIsReadyListener) getActivity());
if (savedInstanceState != null) {
presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY));
}
if (savedInstanceState != null) {
presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY));
}
return view;
}
@ -61,14 +59,6 @@ public class ExamsFragment extends BaseFragment implements ExamsContract.View {
}
}
@Override
public void onError(String message) {
if (getActivity() != null) {
Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager),
message, Snackbar.LENGTH_LONG).show();
}
}
@Override
public void setActivityTitle() {
setTitle(getString(R.string.exams_text));
@ -107,6 +97,6 @@ public class ExamsFragment extends BaseFragment implements ExamsContract.View {
@Override
public void onDestroyView() {
super.onDestroyView();
presenter.onDestroy();
presenter.detachView();
}
}

View File

@ -0,0 +1,32 @@
package io.github.wulkanowy.ui.main.exams;
import javax.inject.Named;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.android.ContributesAndroidInjector;
import io.github.wulkanowy.di.scopes.PerChildFragment;
import io.github.wulkanowy.di.scopes.PerFragment;
import io.github.wulkanowy.ui.base.BasePagerAdapter;
import io.github.wulkanowy.ui.main.exams.tab.ExamsTabFragment;
import io.github.wulkanowy.ui.main.exams.tab.ExamsTabModule;
@Module
public abstract class ExamsModule {
@PerFragment
@Binds
abstract ExamsContract.Presenter provideExamsPresneter(ExamsPresenter examsPresenter);
@Named("Exams")
@PerFragment
@Provides
static BasePagerAdapter providePagerAdapter(ExamsFragment fragment) {
return new BasePagerAdapter(fragment.getChildFragmentManager());
}
@PerChildFragment
@ContributesAndroidInjector(modules = ExamsTabModule.class)
abstract ExamsTabFragment bindExamsTabFragment();
}

View File

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

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.main.exams;
package io.github.wulkanowy.ui.main.exams.tab;
import android.view.View;
import android.widget.TextView;
@ -22,7 +22,7 @@ public class ExamsHeaderItem extends AbstractHeaderItem<ExamsHeaderItem.HeaderVi
private Day day;
public ExamsHeaderItem(Day day) {
ExamsHeaderItem(Day day) {
this.day = day;
}

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.main.exams;
package io.github.wulkanowy.ui.main.exams.tab;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
@ -18,6 +18,7 @@ import eu.davidea.flexibleadapter.items.IFlexible;
import eu.davidea.viewholders.FlexibleViewHolder;
import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.Exam;
import io.github.wulkanowy.ui.main.exams.ExamsDialogFragment;
public class ExamsSubItem
extends AbstractSectionableItem<ExamsSubItem.SubItemViewHolder, ExamsHeaderItem> {

View File

@ -1,8 +1,7 @@
package io.github.wulkanowy.ui.main.exams;
package io.github.wulkanowy.ui.main.exams.tab;
import java.util.List;
import io.github.wulkanowy.di.annotations.PerFragment;
import io.github.wulkanowy.ui.base.BaseContract;
public interface ExamsTabContract {
@ -20,7 +19,6 @@ public interface ExamsTabContract {
void updateAdapterList(List<ExamsSubItem> headerItems);
}
@PerFragment
interface Presenter extends BaseContract.Presenter<View> {
void onFragmentActivated(boolean isSelected);

View File

@ -1,8 +1,7 @@
package io.github.wulkanowy.ui.main.exams;
package io.github.wulkanowy.ui.main.exams.tab;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@ -14,11 +13,9 @@ import java.util.List;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
import io.github.wulkanowy.R;
import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.ui.base.BaseFragment;
public class ExamsTabFragment extends BaseFragment implements ExamsTabContract.View,
@ -60,26 +57,21 @@ public class ExamsTabFragment extends BaseFragment implements ExamsTabContract.V
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_exams_tab, container, false);
injectViews(view);
FragmentComponent component = getFragmentComponent();
if (component != null) {
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
if (getArguments() != null) {
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
}
presenter.onStart(this);
presenter.onFragmentActivated(isFragmentVisible);
if (getArguments() != null) {
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
}
presenter.attachView(this);
presenter.onFragmentActivated(isFragmentVisible);
return view;
}
@Override
protected void setUpOnViewCreated(View fragmentView) {
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
adapter.setDisplayHeadersAtStartUp(true);
recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext()));
recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext()));
recyclerView.setAdapter(adapter);
refreshLayout.setColorSchemeResources(android.R.color.black);
@ -107,7 +99,7 @@ public class ExamsTabFragment extends BaseFragment implements ExamsTabContract.V
@Override
public void onRefreshSuccess() {
onError(R.string.sync_completed);
showMessage(R.string.sync_completed);
}
@Override
@ -125,17 +117,10 @@ public class ExamsTabFragment extends BaseFragment implements ExamsTabContract.V
progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}
@Override
public void onError(String message) {
if (getActivity() != null) {
Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager),
message, Snackbar.LENGTH_LONG).show();
}
}
@Override
public void onDestroyView() {
presenter.detachView();
super.onDestroyView();
presenter.onDestroy();
}
}

View File

@ -0,0 +1,21 @@
package io.github.wulkanowy.ui.main.exams.tab;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import io.github.wulkanowy.di.scopes.PerChildFragment;
@Module
public abstract class ExamsTabModule {
@PerChildFragment
@Binds
abstract ExamsTabContract.Presenter provideExamsTabPresenter(ExamsTabPresenter examsTabPresenter);
@PerChildFragment
@Provides
static FlexibleAdapter<ExamsSubItem> provideAdapter() {
return new FlexibleAdapter<>(null);
}
}

View File

@ -1,4 +1,6 @@
package io.github.wulkanowy.ui.main.exams;
package io.github.wulkanowy.ui.main.exams.tab;
import android.support.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
@ -10,6 +12,7 @@ import io.github.wulkanowy.data.db.dao.entities.Day;
import io.github.wulkanowy.data.db.dao.entities.Exam;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.utils.FabricUtils;
import io.github.wulkanowy.utils.async.AbstractTask;
import io.github.wulkanowy.utils.async.AsyncListeners;
@ -28,13 +31,13 @@ public class ExamsTabPresenter extends BasePresenter<ExamsTabContract.View>
private boolean isFirstSight = false;
@Inject
public ExamsTabPresenter(RepositoryContract repository) {
ExamsTabPresenter(RepositoryContract repository) {
super(repository);
}
@Override
public void onStart(ExamsTabContract.View view) {
super.onStart(view);
public void attachView(@NonNull ExamsTabContract.View view) {
super.attachView(view);
getView().showProgressBar(true);
getView().showNoItem(false);
}
@ -64,7 +67,7 @@ public class ExamsTabPresenter extends BasePresenter<ExamsTabContract.View>
refreshTask.setOnRefreshListener(this);
refreshTask.execute();
} else {
getView().onNoNetworkError();
getView().showNoNetworkMessage();
getView().hideRefreshingBar();
}
}
@ -90,9 +93,11 @@ public class ExamsTabPresenter extends BasePresenter<ExamsTabContract.View>
getView().onRefreshSuccess();
} else {
getView().onError(getRepository().getResRepo().getErrorLoginMessage(exception));
getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception));
}
getView().hideRefreshingBar();
FabricUtils.logRefresh("Exams", result, date);
}
@Override
@ -154,9 +159,9 @@ public class ExamsTabPresenter extends BasePresenter<ExamsTabContract.View>
}
@Override
public void onDestroy() {
public void detachView() {
isFirstSight = false;
cancelAsyncTasks();
super.onDestroy();
super.detachView();
}
}

View File

@ -1,10 +1,10 @@
package io.github.wulkanowy.ui.main.grades;
import android.support.annotation.NonNull;
import android.support.v4.widget.SwipeRefreshLayout;
import java.util.List;
import io.github.wulkanowy.di.annotations.PerActivity;
import io.github.wulkanowy.ui.base.BaseContract;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
@ -30,15 +30,16 @@ public interface GradesContract {
}
@PerActivity
interface Presenter extends BaseContract.Presenter<View> {
void onFragmentVisible(boolean isVisible);
void onRefresh();
void onStart(View view, OnFragmentIsReadyListener listener);
void attachView(@NonNull View view, OnFragmentIsReadyListener listener);
void onSemesterChange(int which);
void onSemesterSwitchActive();
}
}

View File

@ -4,7 +4,6 @@ import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.RecyclerView;
@ -20,11 +19,9 @@ import java.util.List;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
import io.github.wulkanowy.R;
import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
@ -55,14 +52,9 @@ public class GradesFragment extends BaseFragment implements GradesContract.View
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_grades, container, false);
injectViews(view);
FragmentComponent component = getFragmentComponent();
if (component != null) {
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
presenter.onStart(this, (OnFragmentIsReadyListener) getActivity());
}
presenter.attachView(this, (OnFragmentIsReadyListener) getActivity());
return view;
}
@ -80,6 +72,7 @@ public class GradesFragment extends BaseFragment implements GradesContract.View
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_filter) {
presenter.onSemesterSwitchActive();
CharSequence[] items = new CharSequence[]{
getResources().getString(R.string.semester_text, 1),
getResources().getString(R.string.semester_text, 2),
@ -101,14 +94,14 @@ public class GradesFragment extends BaseFragment implements GradesContract.View
}
@Override
protected void setUpOnViewCreated(View fragmentView) {
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
noItemView.setVisibility(View.GONE);
adapter.setAutoCollapseOnExpand(true);
adapter.setAutoScrollOnExpand(true);
adapter.expandItemsAtStartUp();
recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext()));
recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext()));
recyclerView.setAdapter(adapter);
refreshLayout.setColorSchemeResources(android.R.color.black);
@ -154,25 +147,17 @@ public class GradesFragment extends BaseFragment implements GradesContract.View
@Override
public void onRefreshSuccessNoGrade() {
onError(R.string.snackbar_no_grades);
showMessage(R.string.snackbar_no_grades);
}
@Override
public void onRefreshSuccess(int number) {
onError(getString(R.string.snackbar_new_grade, number));
}
@Override
public void onError(String message) {
if (getActivity() != null) {
Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager),
message, Snackbar.LENGTH_LONG).show();
}
showMessage(getString(R.string.snackbar_new_grade, number));
}
@Override
public void onDestroyView() {
presenter.onDestroy();
presenter.detachView();
super.onDestroyView();
}
}

View File

@ -0,0 +1,18 @@
package io.github.wulkanowy.ui.main.grades;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import eu.davidea.flexibleadapter.FlexibleAdapter;
@Module
public abstract class GradesModule {
@Binds
abstract GradesContract.Presenter provideGradesPresenter(GradesPresenter gradesPresenter);
@Provides
static FlexibleAdapter<GradeHeaderItem> provideGradesAdapter() {
return new FlexibleAdapter<>(null);
}
}

View File

@ -1,5 +1,12 @@
package io.github.wulkanowy.ui.main.grades;
import android.support.annotation.NonNull;
import com.crashlytics.android.answers.Answers;
import com.crashlytics.android.answers.CustomEvent;
import org.threeten.bp.LocalDate;
import java.util.ArrayList;
import java.util.List;
@ -10,6 +17,7 @@ import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.utils.FabricUtils;
import io.github.wulkanowy.utils.async.AbstractTask;
import io.github.wulkanowy.utils.async.AsyncListeners;
@ -35,8 +43,8 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
}
@Override
public void onStart(GradesContract.View view, OnFragmentIsReadyListener listener) {
super.onStart(view);
public void attachView(@NonNull GradesContract.View view, OnFragmentIsReadyListener listener) {
super.attachView(view);
this.listener = listener;
if (getView().isMenuVisible()) {
@ -53,12 +61,19 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
}
}
@Override
public void onSemesterSwitchActive() {
cancelAsyncTasks();
}
@Override
public void onSemesterChange(int which) {
semesterName = which + 1;
getView().setCurrentSemester(which);
reloadGrades();
Answers.getInstance().logCustom(new CustomEvent("Semester change")
.putCustomAttribute("Name", semesterName));
}
private void reloadGrades() {
@ -81,7 +96,7 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
refreshTask.setOnRefreshListener(this);
refreshTask.execute();
} else {
getView().onNoNetworkError();
getView().showNoNetworkMessage();
getView().hideRefreshingBar();
}
}
@ -100,8 +115,8 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
}
@Override
public void onEndRefreshAsync(boolean success, Exception exception) {
if (success) {
public void onEndRefreshAsync(boolean result, Exception exception) {
if (result) {
reloadGrades();
int numberOfNewGrades = getRepository().getDbRepo().getNewGrades(semesterName).size();
@ -112,9 +127,11 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
getView().onRefreshSuccess(numberOfNewGrades);
}
} else {
getView().onError(getRepository().getResRepo().getErrorLoginMessage(exception));
getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception));
}
getView().hideRefreshingBar();
FabricUtils.logRefresh("Grades", result, LocalDate.now().toString());
}
@Override
@ -156,10 +173,7 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
listener.onFragmentIsReady();
}
@Override
public void onDestroy() {
isFirstSight = false;
private void cancelAsyncTasks() {
if (refreshTask != null) {
refreshTask.cancel(true);
refreshTask = null;
@ -168,6 +182,12 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
loadingTask.cancel(true);
loadingTask = null;
}
super.onDestroy();
}
@Override
public void detachView() {
isFirstSight = false;
cancelAsyncTasks();
super.detachView();
}
}

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.main.timetable;
import io.github.wulkanowy.di.annotations.PerFragment;
import android.support.annotation.NonNull;
import io.github.wulkanowy.ui.base.BaseContract;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
@ -21,12 +22,11 @@ public interface TimetableContract {
void setThemeForTab(int position);
}
@PerFragment
interface Presenter extends BaseContract.Presenter<View> {
void onFragmentActivated(boolean isVisible);
void onStart(View view, OnFragmentIsReadyListener listener);
void attachView(@NonNull View view, OnFragmentIsReadyListener listener);
void setRestoredPosition(int position);
}

View File

@ -11,14 +11,14 @@ import android.view.View;
import android.view.ViewGroup;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.github.wulkanowy.R;
import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.base.BasePagerAdapter;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.timetable.tab.TimetableTabFragment;
public class TimetableFragment extends BaseFragment implements TimetableContract.View {
@ -30,6 +30,7 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
@BindView(R.id.timetable_fragment_tab_layout)
TabLayout tabLayout;
@Named("Timetable")
@Inject
BasePagerAdapter pagerAdapter;
@ -40,16 +41,12 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_timetable, container, false);
injectViews(view);
FragmentComponent component = getFragmentComponent();
if (component != null) {
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
presenter.onStart(this, (OnFragmentIsReadyListener) getActivity());
presenter.attachView(this, (OnFragmentIsReadyListener) getActivity());
if (savedInstanceState != null) {
presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY));
}
if (savedInstanceState != null) {
presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY));
}
return view;
}
@ -91,14 +88,6 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
setTitle(getString(R.string.timetable_text));
}
@Override
public void onError(String message) {
if (getActivity() != null) {
Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager),
message, Snackbar.LENGTH_LONG).show();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem());
@ -107,7 +96,7 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
@Override
public void onDestroyView() {
presenter.onDestroy();
presenter.detachView();
super.onDestroyView();
}
}

View File

@ -0,0 +1,32 @@
package io.github.wulkanowy.ui.main.timetable;
import javax.inject.Named;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.android.ContributesAndroidInjector;
import io.github.wulkanowy.di.scopes.PerChildFragment;
import io.github.wulkanowy.di.scopes.PerFragment;
import io.github.wulkanowy.ui.base.BasePagerAdapter;
import io.github.wulkanowy.ui.main.timetable.tab.TimetableTabFragment;
import io.github.wulkanowy.ui.main.timetable.tab.TimetableTabModule;
@Module
public abstract class TimetableModule {
@Named("Timetable")
@PerFragment
@Provides
static BasePagerAdapter providePagerAdapter(TimetableFragment fragment) {
return new BasePagerAdapter(fragment.getChildFragmentManager());
}
@PerFragment
@Binds
abstract TimetableContract.Presenter provideTimetablePresenter(TimetablePresenter timetablePresenter);
@PerChildFragment
@ContributesAndroidInjector(modules = TimetableTabModule.class)
abstract TimetableTabFragment bindTimetableTabFragment();
}

View File

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

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.main.timetable;
package io.github.wulkanowy.ui.main.timetable.tab;
import android.content.Context;
import android.content.res.TypedArray;

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.main.timetable;
package io.github.wulkanowy.ui.main.timetable.tab;
import android.content.Context;
import android.graphics.Paint;
@ -21,6 +21,7 @@ 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;
import io.github.wulkanowy.ui.main.timetable.TimetableDialogFragment;
public class TimetableSubItem
@ -106,7 +107,7 @@ public class TimetableSubItem
lessonName.setText(lesson.getSubject());
lessonTime.setText(getLessonTimeString());
numberOfLesson.setText(lesson.getNumber());
numberOfLesson.setText(String.valueOf(lesson.getNumber()));
room.setText(getRoomString());
alert.setVisibility(lesson.getMovedOrCanceled() || lesson.getNewMovedInOrChanged()
? View.VISIBLE : View.INVISIBLE);

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.main.timetable;
package io.github.wulkanowy.ui.main.timetable.tab;
import java.util.List;

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.main.timetable;
package io.github.wulkanowy.ui.main.timetable.tab;
import android.os.Bundle;
import android.support.annotation.NonNull;
@ -16,11 +16,9 @@ import java.util.List;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
import io.github.wulkanowy.R;
import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.ui.base.BaseFragment;
public class TimetableTabFragment extends BaseFragment implements TimetableTabContract.View,
@ -65,33 +63,27 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_timetable_tab, container, false);
injectViews(view);
FragmentComponent component = getFragmentComponent();
if (component != null) {
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
if (getArguments() != null) {
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
}
presenter.onStart(this);
presenter.onFragmentActivated(isFragmentVisible);
if (getArguments() != null) {
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
}
presenter.attachView(this);
presenter.onFragmentActivated(isFragmentVisible);
return view;
}
@Override
protected void setUpOnViewCreated(View fragmentView) {
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
adapter.setAutoCollapseOnExpand(true);
adapter.setAutoScrollOnExpand(true);
adapter.expandItemsAtStartUp();
recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext()));
recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext()));
recyclerView.setAdapter(adapter);
refreshLayout.setColorSchemeResources(android.R.color.black);
refreshLayout.setOnRefreshListener(this);
}
@Override
@ -120,7 +112,7 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
@Override
public void onRefreshSuccess() {
onError(R.string.sync_completed);
showMessage(R.string.sync_completed);
}
@Override
@ -138,17 +130,9 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}
@Override
public void onError(String message) {
if (getActivity() != null) {
Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager),
message, Snackbar.LENGTH_LONG).show();
}
}
@Override
public void onDestroyView() {
presenter.onDestroy();
presenter.detachView();
super.onDestroyView();
}
}

View File

@ -0,0 +1,21 @@
package io.github.wulkanowy.ui.main.timetable.tab;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import io.github.wulkanowy.di.scopes.PerChildFragment;
@Module
public abstract class TimetableTabModule {
@PerChildFragment
@Binds
abstract TimetableTabContract.Presenter provideTimetableTabPresneter(TimetableTabPresenter timetableTabPresenter);
@PerChildFragment
@Provides
static FlexibleAdapter<TimetableHeaderItem> provideTimetableAdapter() {
return new FlexibleAdapter<>(null);
}
}

View File

@ -1,6 +1,8 @@
package io.github.wulkanowy.ui.main.timetable;
package io.github.wulkanowy.ui.main.timetable.tab;
import android.support.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
@ -11,6 +13,7 @@ import io.github.wulkanowy.data.db.dao.entities.Day;
import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.utils.FabricUtils;
import io.github.wulkanowy.utils.async.AbstractTask;
import io.github.wulkanowy.utils.async.AsyncListeners;
@ -36,8 +39,8 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
}
@Override
public void onStart(TimetableTabContract.View view) {
super.onStart(view);
public void attachView(@NonNull TimetableTabContract.View view) {
super.attachView(view);
getView().showProgressBar(true);
getView().showNoItem(false);
}
@ -62,7 +65,7 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
refreshTask.setOnRefreshListener(this);
refreshTask.execute();
} else {
getView().onNoNetworkError();
getView().showNoNetworkMessage();
getView().hideRefreshingBar();
}
}
@ -88,9 +91,11 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
getView().onRefreshSuccess();
} else {
getView().onError(getRepository().getResRepo().getErrorLoginMessage(exception));
getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception));
}
getView().hideRefreshingBar();
FabricUtils.logRefresh("Timetable", result, date);
}
@Override
@ -175,9 +180,9 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
}
@Override
public void onDestroy() {
public void detachView() {
isFirstSight = false;
cancelAsyncTasks();
super.onDestroy();
super.detachView();
}
}

View File

@ -4,7 +4,6 @@ import android.os.Bundle;
import javax.inject.Inject;
import butterknife.ButterKnife;
import io.github.wulkanowy.services.jobs.SyncJob;
import io.github.wulkanowy.services.notifies.NotificationService;
import io.github.wulkanowy.ui.base.BaseActivity;
@ -19,16 +18,12 @@ public class SplashActivity extends BaseActivity implements SplashContract.View
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivityComponent().inject(this);
setButterKnife(ButterKnife.bind(this));
presenter.onStart(this);
presenter.attachView(this);
}
@Override
protected void onDestroy() {
presenter.onDestroy();
presenter.detachView();
super.onDestroy();
}
@ -44,11 +39,6 @@ public class SplashActivity extends BaseActivity implements SplashContract.View
finish();
}
@Override
public void startSyncService(int interval, boolean useOnlyWifi) {
SyncJob.start(getApplicationContext(), interval, useOnlyWifi);
}
@Override
public void cancelNotifications() {
new NotificationService(getApplicationContext()).cancelAll();

View File

@ -1,7 +1,5 @@
package io.github.wulkanowy.ui.splash;
import io.github.wulkanowy.di.annotations.PerActivity;
import io.github.wulkanowy.ui.base.BaseContract;
public interface SplashContract {
@ -12,12 +10,9 @@ public interface SplashContract {
void openMainActivity();
void startSyncService(int interval, boolean useOnlyWifi);
void cancelNotifications();
}
@PerActivity
interface Presenter extends BaseContract.Presenter<View> {
}
}

View File

@ -0,0 +1,13 @@
package io.github.wulkanowy.ui.splash;
import dagger.Binds;
import dagger.Module;
import io.github.wulkanowy.di.scopes.PerActivity;
@Module
public abstract class SplashModule {
@PerActivity
@Binds
abstract SplashContract.Presenter provideSplashPresenter(SplashPresenter splashPresenter);
}

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