1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-20 00:19:09 -05:00
This commit is contained in:
Rafał Borcz 2018-03-04 12:49:16 +01:00 committed by Mikołaj Pich
parent a1a3427142
commit 79bdbbbb16
158 changed files with 4369 additions and 3753 deletions

View File

@ -1,5 +1,5 @@
exclude: exclude:
- /app/src/main/java/io/github/wulkanowy/db/dao/entities/.* - /app/src/main/java/io/github/wulkanowy/data/db/dao/entities/.*
component_depth: 1 component_depth: 1
languages: languages:
- java - java

View File

@ -48,7 +48,7 @@ public class Vulcan {
this.login = login; this.login = login;
} }
public String login(String email, String password, String symbol) public void login(String email, String password, String symbol)
throws BadCredentialsException, AccountPermissionException, throws BadCredentialsException, AccountPermissionException,
LoginErrorException, IOException, VulcanOfflineException { LoginErrorException, IOException, VulcanOfflineException {
@ -56,8 +56,6 @@ public class Vulcan {
login = getLogin(); login = getLogin();
this.symbol = login.login(this.email, password, symbol); this.symbol = login.login(this.email, password, symbol);
return this.symbol;
} }
public Vulcan login(String email, String password, String symbol, String id) public Vulcan login(String email, String password, String symbol, String id)
@ -82,6 +80,10 @@ public class Vulcan {
return email; return email;
} }
public String getSymbol() {
return symbol;
}
private void setFullEndpointInfo(String email) { private void setFullEndpointInfo(String email) {
String[] creds = email.split("\\\\"); String[] creds = email.split("\\\\");

View File

@ -27,14 +27,6 @@ public class Login {
this.client = client; this.client = client;
} }
private String getLoginPageUrl() {
return LOGIN_PAGE_URL;
}
private String getLoginEndpointPageUrl() {
return LOGIN_ENDPOINT_PAGE_URL;
}
public String login(String email, String password, String symbol) public String login(String email, String password, String symbol)
throws BadCredentialsException, LoginErrorException, throws BadCredentialsException, LoginErrorException,
AccountPermissionException, IOException, VulcanOfflineException { AccountPermissionException, IOException, VulcanOfflineException {
@ -47,7 +39,7 @@ public class Login {
throws IOException, BadCredentialsException { throws IOException, BadCredentialsException {
this.symbol = symbol; this.symbol = symbol;
Document html = client.postPageByUrl(getLoginPageUrl(), new String[][]{ Document html = client.postPageByUrl(LOGIN_PAGE_URL, new String[][]{
{"LoginName", email}, {"LoginName", email},
{"Password", password} {"Password", password}
}); });
@ -64,8 +56,7 @@ public class Login {
this.symbol = findSymbol(defaultSymbol, certificate); this.symbol = findSymbol(defaultSymbol, certificate);
client.setSymbol(this.symbol); client.setSymbol(this.symbol);
Document html = client.postPageByUrl(getLoginEndpointPageUrl() Document html = client.postPageByUrl(LOGIN_ENDPOINT_PAGE_URL, new String[][]{
.replace("{symbol}", this.symbol), new String[][]{
{"wa", "wsignin1.0"}, {"wa", "wsignin1.0"},
{"wresult", certificate} {"wresult", certificate}
}); });

View File

@ -62,29 +62,29 @@ android {
} }
greendao { greendao {
schemaVersion 19 schemaVersion 20
generateTests = true generateTests = true
} }
dependencies { dependencies {
implementation project(':api') implementation project(':api')
implementation 'com.android.support:appcompat-v7:27.0.2' implementation 'com.android.support:appcompat-v7:27.1.0'
implementation 'com.android.support:design:27.0.2' implementation 'com.android.support:design:27.1.0'
implementation 'com.android.support:support-v4:27.0.2' implementation 'com.android.support:support-v4:27.1.0'
implementation 'com.android.support:recyclerview-v7:27.0.2' implementation 'com.android.support:recyclerview-v7:27.1.0'
implementation 'com.android.support:cardview-v7:27.0.2' implementation 'com.android.support:cardview-v7:27.1.0'
implementation 'com.android.support:customtabs:27.0.2' implementation 'com.android.support:customtabs:27.1.0'
implementation 'com.firebase:firebase-jobdispatcher:0.8.5' implementation 'com.firebase:firebase-jobdispatcher:0.8.5'
implementation 'com.thoughtbot:expandablerecyclerview:1.3'
implementation 'org.apache.commons:commons-lang3:3.7' implementation 'org.apache.commons:commons-lang3:3.7'
implementation 'eu.davidea:flexible-adapter:5.0.0-rc3' implementation 'eu.davidea:flexible-adapter:5.0.0-rc4'
implementation 'eu.davidea:flexible-adapter-ui:1.0.0-b1'
implementation 'org.apache.commons:commons-collections4:4.1' implementation 'org.apache.commons:commons-collections4:4.1'
implementation 'org.greenrobot:greendao:3.2.2' implementation 'org.greenrobot:greendao:3.2.2'
implementation 'com.jakewharton:butterknife:8.8.1' implementation 'com.jakewharton:butterknife:8.8.1'
implementation 'joda-time:joda-time:2.9.9' implementation 'joda-time:joda-time:2.9.9'
implementation 'com.github.javiersantos:AppUpdater:2.6.4'
implementation 'com.google.dagger:dagger-android:2.14.1' implementation 'com.google.dagger:dagger-android:2.14.1'
implementation 'com.google.dagger:dagger-android-support:2.14.1' implementation 'com.google.dagger:dagger-android-support:2.14.1'
implementation 'com.aurelhubert:ahbottomnavigation:2.1.0'
implementation('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') { implementation('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {
transitive = true transitive = true
@ -94,6 +94,7 @@ dependencies {
} }
annotationProcessor 'com.google.dagger:dagger-android-processor:2.14.1' annotationProcessor 'com.google.dagger:dagger-android-processor:2.14.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
debugImplementation 'com.amitshekhar.android:debug-db:1.0.1' debugImplementation 'com.amitshekhar.android:debug-db:1.0.1'

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.db.dao.entities; package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.test.AbstractDaoTestLongPk; import org.greenrobot.greendao.test.AbstractDaoTestLongPk;

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.db.dao.entities; package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.test.AbstractDaoTestLongPk; import org.greenrobot.greendao.test.AbstractDaoTestLongPk;

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.db.dao.entities; package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.test.AbstractDaoTestLongPk; import org.greenrobot.greendao.test.AbstractDaoTestLongPk;

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.db.dao.entities; package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.test.AbstractDaoTestLongPk; import org.greenrobot.greendao.test.AbstractDaoTestLongPk;

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.db.dao.entities; package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.test.AbstractDaoTestLongPk; import org.greenrobot.greendao.test.AbstractDaoTestLongPk;

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.db.dao.entities; package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.test.AbstractDaoTestLongPk; import org.greenrobot.greendao.test.AbstractDaoTestLongPk;

View File

@ -1,50 +0,0 @@
package io.github.wulkanowy.db.dao;
import android.support.test.InstrumentationRegistry;
import org.greenrobot.greendao.database.Database;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import io.github.wulkanowy.db.dao.entities.DaoMaster;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import io.github.wulkanowy.db.dao.entities.Grade;
public class DatabaseAccessTest extends DatabaseAccess {
private static DaoSession daoSession;
@BeforeClass
public static void setUpClass() {
DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(InstrumentationRegistry.getTargetContext()
, "wulkanowyTest-db");
Database database = devOpenHelper.getWritableDb();
daoSession = new DaoMaster(database).newSession();
}
@Before
public void setUp() {
daoSession.getGradeDao().deleteAll();
daoSession.clear();
}
@Test
public void getNewGradesTest() {
daoSession.getGradeDao().insert(new Grade()
.setIsNew(true));
Assert.assertEquals(1, new DatabaseAccess().getNewGrades(daoSession).size());
}
@AfterClass
public static void cleanUp() {
daoSession.getAccountDao().deleteAll();
daoSession.getGradeDao().deleteAll();
daoSession.getSubjectDao().deleteAll();
daoSession.clear();
}
}

View File

@ -1,106 +0,0 @@
package io.github.wulkanowy.services.sync;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.greenrobot.greendao.database.Database;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import java.io.IOException;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.login.AccountPermissionException;
import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.api.login.LoginErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException;
import io.github.wulkanowy.db.dao.entities.Account;
import io.github.wulkanowy.db.dao.entities.AccountDao;
import io.github.wulkanowy.db.dao.entities.DaoMaster;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import io.github.wulkanowy.utils.security.CryptoException;
import io.github.wulkanowy.utils.security.Safety;
@RunWith(AndroidJUnit4.class)
public class CurrentAccountLoginTest {
private static DaoSession daoSession;
private Context context;
private Context targetContext;
@BeforeClass
public static void setUpClass() {
DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(InstrumentationRegistry.getTargetContext(), "wulkanowyTest-db");
Database database = devOpenHelper.getWritableDb();
daoSession = new DaoMaster(database).newSession();
DaoMaster.dropAllTables(database, true);
DaoMaster.createAllTables(database, true);
}
@Before
public void setUp() {
context = InstrumentationRegistry.getContext();
targetContext = InstrumentationRegistry.getTargetContext();
daoSession.getAccountDao().deleteAll();
daoSession.clear();
setUserIdSharePreferences(0);
}
@Test(expected = IOException.class)
public void emptyUserIdTest() throws CryptoException, BadCredentialsException,
AccountPermissionException, IOException, LoginErrorException, VulcanOfflineException {
CurrentAccountLogin currentAccountLogin = new CurrentAccountLogin(context, daoSession, new Vulcan());
currentAccountLogin.loginCurrentUser();
}
@Test
public void loginCurrentUserTest() throws Exception {
AccountDao accountDao = daoSession.getAccountDao();
Safety safety = new Safety();
Long userId = accountDao.insert(new Account()
.setEmail("TEST@TEST")
.setPassword(safety.encrypt("TEST@TEST", "TEST", context))
.setSymbol(""));
setUserIdSharePreferences(userId);
Vulcan vulcan = Mockito.mock(Vulcan.class);
Mockito.when(vulcan.login("TEST@TEST", "TEST", "TEST_SYMBOL", "TEST_ID")).thenReturn(new Vulcan());
CurrentAccountLogin currentAccountLogin = new CurrentAccountLogin(targetContext, daoSession, vulcan);
LoginSession loginSession = currentAccountLogin.loginCurrentUser();
Assert.assertNotNull(loginSession);
Assert.assertEquals(loginSession.getUserId(), userId);
Assert.assertNotNull(loginSession.getDaoSession());
Assert.assertEquals(loginSession.getVulcan(), vulcan);
}
private void setUserIdSharePreferences(long id) {
SharedPreferences sharedPreferences = targetContext.getSharedPreferences("LoginData", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putLong("userId", id);
editor.apply();
}
@AfterClass
public static void cleanUp() {
daoSession.getAccountDao().deleteAll();
daoSession.clear();
}
}

View File

@ -1,99 +0,0 @@
package io.github.wulkanowy.services.sync;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.test.InstrumentationRegistry;
import org.greenrobot.greendao.database.Database;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
import io.github.wulkanowy.api.StudentAndParent;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.user.BasicInformation;
import io.github.wulkanowy.api.user.PersonalData;
import io.github.wulkanowy.db.dao.entities.Account;
import io.github.wulkanowy.db.dao.entities.DaoMaster;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import io.github.wulkanowy.utils.security.Safety;
public class FirstAccountLoginTest {
private static DaoSession daoSession;
private Context targetContext;
@BeforeClass
public static void setUpClass() {
DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(InstrumentationRegistry.getTargetContext(), "wulkanowyTest-db");
Database database = devOpenHelper.getWritableDb();
daoSession = new DaoMaster(database).newSession();
DaoMaster.dropAllTables(database, true);
DaoMaster.createAllTables(database, true);
}
@Before
public void setUp() {
targetContext = InstrumentationRegistry.getTargetContext();
daoSession.getAccountDao().deleteAll();
daoSession.clear();
setUserIdSharePreferences(0);
}
@Test
public void loginTest() throws Exception {
StudentAndParent snp = Mockito.mock(StudentAndParent.class);
Mockito.when(snp.getId()).thenReturn("TEST-ID");
PersonalData personalData = Mockito.mock(PersonalData.class);
Mockito.doReturn("NAME-TEST").when(personalData).getFirstAndLastName();
BasicInformation basicInformation = Mockito.mock(BasicInformation.class);
Mockito.doReturn(personalData).when(basicInformation).getPersonalData();
Vulcan vulcan = Mockito.mock(Vulcan.class);
Mockito.doReturn("TEST-SYMBOL").when(vulcan).login(Mockito.anyString(), Mockito.anyString(), Mockito.anyString());
Mockito.doReturn(snp).when(vulcan).getStudentAndParent();
Mockito.doReturn(basicInformation).when(vulcan).getBasicInformation();
FirstAccountLogin firstAccountLogin = new FirstAccountLogin(targetContext, daoSession, vulcan);
LoginSession loginSession = firstAccountLogin.login("TEST@TEST", "TEST-PASS", "default");
Long userId = targetContext.getSharedPreferences("LoginData", Context.MODE_PRIVATE).getLong("userId", 0);
Assert.assertNotNull(loginSession);
Assert.assertNotEquals(0, userId.longValue());
Assert.assertEquals(loginSession.getUserId(), userId);
Assert.assertNotNull(loginSession.getDaoSession());
Assert.assertEquals(loginSession.getVulcan(), vulcan);
Safety safety = new Safety();
Account account = daoSession.getAccountDao().load(userId);
Assert.assertNotNull(account);
Assert.assertEquals("TEST@TEST", account.getEmail());
Assert.assertEquals("NAME-TEST", account.getName());
Assert.assertEquals("TEST-PASS", safety.decrypt("TEST@TEST", account.getPassword()));
Assert.assertEquals("TEST-SYMBOL", account.getSymbol());
Assert.assertEquals("TEST-ID", account.getSnpId());
}
private void setUserIdSharePreferences(long id) {
SharedPreferences sharedPreferences = targetContext.getSharedPreferences("LoginData", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putLong("userId", id);
editor.apply();
}
@AfterClass
public static void cleanUp() {
daoSession.getAccountDao().deleteAll();
daoSession.clear();
}
}

View File

@ -1,88 +0,0 @@
package io.github.wulkanowy.services.sync;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.greenrobot.greendao.database.Database;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.grades.Grade;
import io.github.wulkanowy.api.grades.GradesList;
import io.github.wulkanowy.db.dao.entities.Account;
import io.github.wulkanowy.db.dao.entities.DaoMaster;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import io.github.wulkanowy.db.dao.entities.Subject;
@RunWith(AndroidJUnit4.class)
public class GradeSyncTest {
private static DaoSession daoSession;
@BeforeClass
public static void setUpClass() {
DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(InstrumentationRegistry.getTargetContext(), "wulkanowyTest-db");
Database database = devOpenHelper.getWritableDb();
daoSession = new DaoMaster(database).newSession();
DaoMaster.dropAllTables(database, true);
DaoMaster.createAllTables(database, true);
}
@Before
public void setUp() {
daoSession.getAccountDao().deleteAll();
daoSession.getGradeDao().deleteAll();
daoSession.getSubjectDao().deleteAll();
daoSession.clear();
}
@Test
public void syncGradesEmptyDatabaseTest() throws Exception {
Long userId = daoSession.getAccountDao().insert(new Account().setEmail("TEST@TEST"));
Long subjectId = daoSession.getSubjectDao().insert(new Subject().setName("Matematyka").setUserId(userId));
List<Grade> gradeList = new ArrayList<>();
gradeList.add(new Grade().setSubject("Matematyka").setValue("5"));
GradesList gradesListApi = Mockito.mock(GradesList.class);
Mockito.doReturn(gradeList).when(gradesListApi).getAll();
Vulcan vulcan = Mockito.mock(Vulcan.class);
Mockito.doReturn(gradesListApi).when(vulcan).getGradesList();
LoginSession loginSession = Mockito.mock(LoginSession.class);
Mockito.doReturn(vulcan).when(loginSession).getVulcan();
Mockito.doReturn(daoSession).when(loginSession).getDaoSession();
Mockito.doReturn(userId).when(loginSession).getUserId();
GradesSync gradesSync = new GradesSync();
gradesSync.sync(loginSession);
io.github.wulkanowy.db.dao.entities.Grade grade = daoSession.getGradeDao().load(1L);
Assert.assertNotNull(grade);
Assert.assertEquals(userId, grade.getUserId());
Assert.assertEquals(subjectId, grade.getSubjectId());
Assert.assertEquals("Matematyka", grade.getSubject());
Assert.assertEquals("5", grade.getValue());
Assert.assertFalse(grade.getIsNew());
}
@AfterClass
public static void cleanUp() {
daoSession.getAccountDao().deleteAll();
daoSession.getGradeDao().deleteAll();
daoSession.getSubjectDao().deleteAll();
daoSession.clear();
}
}

View File

@ -1,82 +0,0 @@
package io.github.wulkanowy.services.sync;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.greenrobot.greendao.database.Database;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.grades.SubjectsList;
import io.github.wulkanowy.db.dao.entities.DaoMaster;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import io.github.wulkanowy.db.dao.entities.Subject;
@RunWith(AndroidJUnit4.class)
public class SubjectSyncTest {
private static DaoSession daoSession;
@BeforeClass
public static void setUpClass() {
DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(InstrumentationRegistry.getTargetContext(), "wulkanowyTest-db");
Database database = devOpenHelper.getWritableDb();
daoSession = new DaoMaster(database).newSession();
DaoMaster.dropAllTables(database, true);
DaoMaster.createAllTables(database, true);
}
@Before
public void setUp() {
daoSession.getSubjectDao().deleteAll();
daoSession.clear();
}
@Test
public void syncSubjectTest() throws Exception {
List<io.github.wulkanowy.api.grades.Subject> subjectList = new ArrayList<>();
subjectList.add(new io.github.wulkanowy.api.grades.Subject()
.setName("Matematyka")
.setFinalRating("5")
.setPredictedRating("4"));
SubjectsList subjectsListApi = Mockito.mock(SubjectsList.class);
Mockito.doReturn(subjectList).when(subjectsListApi).getAll();
Vulcan vulcan = Mockito.mock(Vulcan.class);
Mockito.doReturn(subjectsListApi).when(vulcan).getSubjectsList();
LoginSession loginSession = Mockito.mock(LoginSession.class);
Mockito.doReturn(vulcan).when(loginSession).getVulcan();
Mockito.doReturn(2L).when(loginSession).getUserId();
Mockito.doReturn(daoSession).when(loginSession).getDaoSession();
SubjectsSync subjectsSync = new SubjectsSync();
subjectsSync.sync(loginSession);
Subject subject = daoSession.getSubjectDao().load(1L);
Assert.assertNotNull(subject);
Assert.assertEquals(2, subject.getUserId().longValue());
Assert.assertEquals("Matematyka", subject.getName());
Assert.assertEquals("5", subject.getFinalRating());
Assert.assertEquals("4", subject.getPredictedRating());
}
@AfterClass
public static void cleanUp() {
daoSession.getSubjectDao().deleteAll();
daoSession.clear();
}
}

View File

@ -1,95 +0,0 @@
package io.github.wulkanowy.services.sync;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.greenrobot.greendao.database.Database;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.timetable.Day;
import io.github.wulkanowy.api.timetable.Lesson;
import io.github.wulkanowy.api.timetable.Timetable;
import io.github.wulkanowy.api.timetable.Week;
import io.github.wulkanowy.db.dao.entities.Account;
import io.github.wulkanowy.db.dao.entities.DaoMaster;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@RunWith(AndroidJUnit4.class)
public class TimetableSyncTest {
private static DaoSession daoSession;
@BeforeClass
public static void setUpClass() {
DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(InstrumentationRegistry.getTargetContext(), "wulkanowyTest-db");
Database database = devOpenHelper.getWritableDb();
daoSession = new DaoMaster(database).newSession();
DaoMaster.dropAllTables(database, true);
DaoMaster.createAllTables(database, true);
}
@Test
public void syncTimetableEmptyDatabaseTest() throws Exception {
Long userId = daoSession.getAccountDao().insert(new Account().setEmail("TEST@TEST"));
List<Day> dayList = new ArrayList<>();
dayList.add(new Day()
.setDate("20.12.2012")
.setLesson(new Lesson().setSubject("Matematyka").setRoom("20")));
Week week = new Week().setDays(dayList);
List<Day> nextDayList = new ArrayList<>();
dayList.add(new Day()
.setDate("24.11.2013")
.setLesson(new Lesson().setSubject("Matematyka").setRoom("22")));
Week nextWeek = new Week().setDays(nextDayList);
Timetable timetable = mock(Timetable.class);
doReturn(week).when(timetable).getWeekTable();
doReturn(nextWeek).when(timetable).getWeekTable(anyString());
Vulcan vulcan = mock(Vulcan.class);
doReturn(timetable).when(vulcan).getTimetable();
LoginSession loginSession = mock(LoginSession.class);
doReturn(vulcan).when(loginSession).getVulcan();
doReturn(daoSession).when(loginSession).getDaoSession();
doReturn(userId).when(loginSession).getUserId();
TimetableSync timetableSync = new TimetableSync();
timetableSync.sync(loginSession, null);
List<io.github.wulkanowy.db.dao.entities.Day> dayEntityList = daoSession.getDayDao().loadAll();
List<io.github.wulkanowy.db.dao.entities.Lesson> lessonEntityList = dayEntityList.get(0).getLessons();
Assert.assertNotNull(dayEntityList.get(0));
Assert.assertEquals(userId, dayEntityList.get(0).getUserId());
Assert.assertEquals(1L, lessonEntityList.get(0).getDayId().longValue());
Assert.assertEquals("Matematyka", lessonEntityList.get(0).getSubject());
Assert.assertEquals("20", lessonEntityList.get(0).getRoom());
Assert.assertEquals("20.12.2012", dayEntityList.get(0).getDate());
}
@AfterClass
public static void cleanUp() {
daoSession.getAccountDao().deleteAll();
daoSession.getDayDao().deleteAll();
daoSession.getLessonDao().deleteAll();
daoSession.getWeekDao().deleteAll();
daoSession.clear();
}
}

View File

@ -1,30 +0,0 @@
package io.github.wulkanowy.services.sync;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
@RunWith(AndroidJUnit4.class)
public class VulcanSyncTest {
@Test(expected = IOException.class)
public void syncNoLoginSessionSubjectTest() throws IOException {
VulcanSync vulcanSync = new VulcanSync(new LoginSession());
vulcanSync.syncSubjectsAndGrades();
}
@Test(expected = IOException.class)
public void syncNoLoginSessionGradeTest() throws IOException {
VulcanSync vulcanSync = new VulcanSync(new LoginSession());
vulcanSync.syncGrades();
}
@Test(expected = IOException.class)
public void syncNoLoginSessionTimetableTest() throws IOException {
VulcanSync vulcanSync = new VulcanSync(new LoginSession());
vulcanSync.syncTimetable();
}
}

View File

@ -1,25 +0,0 @@
package io.github.wulkanowy.utils.security;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SdkSuppress;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SafetyTest {
@Test
@SdkSuppress(minSdkVersion = 18)
public void encryptDecryptTest() throws Exception {
Context targetContext = InstrumentationRegistry.getTargetContext();
Safety safety = new Safety();
Assert.assertEquals("PASS", safety.decrypt("TEST", safety.encrypt("TEST", "PASS", targetContext)));
}
}

View File

@ -1,59 +1,25 @@
package io.github.wulkanowy.utils.security; package io.github.wulkanowy.utils.security;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.support.test.InstrumentationRegistry; import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SdkSuppress; import android.support.test.filters.SdkSuppress;
import android.support.test.filters.SmallTest; import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@SmallTest @SmallTest
@SdkSuppress(minSdkVersion = 18)
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class ScramblerTest { public class ScramblerTest {
private Context targetContext;
private Scrambler scramblerLoad = new Scrambler();
@Before
public void setUp() throws CryptoException {
targetContext = InstrumentationRegistry.getTargetContext();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
scramblerLoad.loadKeyStore();
}
}
@Test(expected = CryptoException.class)
@SdkSuppress(maxSdkVersion = 17)
public void testNoSuchAlgorithm() throws CryptoException {
scramblerLoad.loadKeyStore();
}
@Test @Test
public void decryptEncryptStringTest() throws CryptoException { @SdkSuppress(minSdkVersion = 18)
scramblerLoad.generateNewKey("TEST", targetContext); public void encryptDecryptTest() throws Exception {
Assert.assertEquals("pass", Context targetContext = InstrumentationRegistry.getTargetContext();
scramblerLoad.decryptString("TEST", scramblerLoad.encryptString("TEST", "pass")));
}
@Test(expected = CryptoException.class) Assert.assertEquals("PASS", Scrambler.decrypt("TEST",
public void decryptEmptyTest() throws CryptoException { Scrambler.encrypt("TEST", "PASS", targetContext)));
scramblerLoad.decryptString("", "");
}
@Test(expected = CryptoException.class)
public void encryptEmptyTest() throws CryptoException {
scramblerLoad.encryptString("", "");
}
@Test(expected = CryptoException.class)
public void generateNewKeyEmptyTest() throws CryptoException {
scramblerLoad.generateNewKey("", targetContext);
} }
} }

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="io.github.wulkanowy" package="io.github.wulkanowy"
android:installLocation="internalOnly"> android:installLocation="internalOnly">
@ -11,8 +10,6 @@
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-sdk tools:overrideLibrary="com.thoughtbot.expandablerecyclerview" />
<application <application
android:name=".WulkanowyApp" android:name=".WulkanowyApp"
android:allowBackup="false" android:allowBackup="false"
@ -25,7 +22,7 @@
android:name=".ui.splash.SplashActivity" android:name=".ui.splash.SplashActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:noHistory="true" android:noHistory="true"
android:theme="@style/WulkanowyTheme.noActionBar"> android:theme="@style/WulkanowyTheme.SplashTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -38,12 +35,12 @@
android:label="@string/title_activity_login" android:label="@string/title_activity_login"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity <activity
android:name=".ui.main.DashboardActivity" android:name=".ui.main.MainActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/activity_dashboard_text" /> android:label="@string/activity_dashboard_text" />
<service <service
android:name=".services.jobs.FullSyncJob$SyncService" android:name=".services.SyncJob"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE" /> <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE" />

View File

@ -1,50 +1,48 @@
package io.github.wulkanowy; package io.github.wulkanowy;
import android.app.Application; import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import com.crashlytics.android.Crashlytics; import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.core.CrashlyticsCore; import com.crashlytics.android.core.CrashlyticsCore;
import org.greenrobot.greendao.query.QueryBuilder; import org.greenrobot.greendao.query.QueryBuilder;
import javax.inject.Inject;
import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.utils.Log; import eu.davidea.flexibleadapter.utils.Log;
import io.fabric.sdk.android.Fabric; import io.fabric.sdk.android.Fabric;
import io.github.wulkanowy.db.dao.entities.DaoMaster; import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.db.dao.entities.DaoSession; import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.di.component.ApplicationComponent;
import io.github.wulkanowy.di.component.DaggerApplicationComponent;
import io.github.wulkanowy.di.modules.ApplicationModule;
public class WulkanowyApp extends Application { public class WulkanowyApp extends Application {
public static final String DEBUG_TAG = "WulaknowyActivity"; protected ApplicationComponent applicationComponent;
private DaoSession daoSession; @Inject
RepositoryContract repository;
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
applicationComponent = DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(this))
.build();
applicationComponent.inject(this);
initializeFabric();
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
enableDebugLog(); enableDebugLog();
} }
initializeFabric();
DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(this, "wulkanowy-db");
daoSession = new DaoMaster(devOpenHelper.getWritableDb()).newSession();
int schemaVersion = getSharedPreferences("LoginData", Context.MODE_PRIVATE).getInt("schemaVersion", 0);
if (DaoMaster.SCHEMA_VERSION != schemaVersion) {
SharedPreferences sharedPreferences = getSharedPreferences("LoginData", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putLong("userId", 0);
editor.putInt("schemaVersion", DaoMaster.SCHEMA_VERSION);
editor.apply();
} }
public ApplicationComponent getApplicationComponent() {
return applicationComponent;
} }
private void enableDebugLog() { private void enableDebugLog() {
@ -62,6 +60,6 @@ public class WulkanowyApp extends Application {
} }
public DaoSession getDaoSession() { public DaoSession getDaoSession() {
return daoSession; return null;
} }
} }

View File

@ -0,0 +1,143 @@
package io.github.wulkanowy.data;
import java.io.IOException;
import java.text.ParseException;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.api.login.AccountPermissionException;
import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException;
import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.GradeDao;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.data.db.resources.ResourcesContract;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.data.sync.SyncContract;
import io.github.wulkanowy.data.sync.login.LoginSyncContract;
import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract;
import io.github.wulkanowy.di.annotations.SyncGrades;
import io.github.wulkanowy.di.annotations.SyncSubjects;
import io.github.wulkanowy.utils.security.CryptoException;
@Singleton
public class Repository implements RepositoryContract {
private final SharedPrefContract sharedPref;
private final ResourcesContract resources;
private final DaoSession daoSession;
private final LoginSyncContract loginSync;
private final TimetableSyncContract timetableSync;
private final SyncContract gradeSync;
private final SyncContract subjectSync;
@Inject
Repository(SharedPrefContract sharedPref,
ResourcesContract resources,
DaoSession daoSession,
LoginSyncContract loginSync,
TimetableSyncContract timetableSync,
@SyncGrades SyncContract gradeSync,
@SyncSubjects SyncContract subjectSync) {
this.sharedPref = sharedPref;
this.resources = resources;
this.daoSession = daoSession;
this.loginSync = loginSync;
this.timetableSync = timetableSync;
this.gradeSync = gradeSync;
this.subjectSync = subjectSync;
}
@Override
public long getCurrentUserId() {
return sharedPref.getCurrentUserId();
}
@Override
public String[] getSymbolsKeysArray() {
return resources.getSymbolsKeysArray();
}
@Override
public String[] getSymbolsValuesArray() {
return resources.getSymbolsValuesArray();
}
@Override
public String getErrorLoginMessage(Exception e) {
return resources.getErrorLoginMessage(e);
}
@Override
public void loginUser(String email, String password, String symbol)
throws NotLoggedInErrorException, AccountPermissionException, IOException,
CryptoException, VulcanOfflineException, BadCredentialsException {
loginSync.loginUser(email, password, symbol);
}
@Override
public void loginCurrentUser() throws NotLoggedInErrorException, AccountPermissionException,
IOException, CryptoException, VulcanOfflineException, BadCredentialsException {
loginSync.loginCurrentUser();
}
@Override
public void syncGrades() throws NotLoggedInErrorException, IOException, ParseException {
gradeSync.sync();
}
@Override
public void syncSubjects() throws NotLoggedInErrorException, IOException, ParseException {
subjectSync.sync();
}
@Override
public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException {
timetableSync.syncTimetable();
}
@Override
public void syncTimetable(String date) throws NotLoggedInErrorException, IOException, ParseException {
timetableSync.syncTimetable(date);
}
@Override
public void syncAll() throws NotLoggedInErrorException, IOException, ParseException {
syncSubjects();
syncGrades();
syncTimetable();
}
@Override
public Account getCurrentUser() {
return daoSession.getAccountDao().load(sharedPref.getCurrentUserId());
}
@Override
public Week getWeek(String date) {
return daoSession.getWeekDao().queryBuilder()
.where(WeekDao.Properties.StartDayDate.eq(date),
WeekDao.Properties.UserId.eq(getCurrentUserId()))
.unique();
}
@Override
public List<Grade> getNewGrades() {
return daoSession.getGradeDao().queryBuilder()
.where(GradeDao.Properties.IsNew.eq(1))
.list();
}
}

View File

@ -0,0 +1,34 @@
package io.github.wulkanowy.data;
import java.io.IOException;
import java.text.ParseException;
import java.util.List;
import javax.inject.Singleton;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.resources.ResourcesContract;
import io.github.wulkanowy.data.sync.login.LoginSyncContract;
import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract;
@Singleton
public interface RepositoryContract extends ResourcesContract, LoginSyncContract,
TimetableSyncContract {
long getCurrentUserId();
void syncGrades() throws NotLoggedInErrorException, IOException, ParseException;
void syncSubjects() throws NotLoggedInErrorException, IOException, ParseException;
void syncAll() throws NotLoggedInErrorException, IOException, ParseException;
Account getCurrentUser();
Week getWeek(String date);
List<Grade> getNewGrades();
}

View File

@ -0,0 +1,19 @@
package io.github.wulkanowy.data.db.dao;
import android.content.Context;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.data.db.dao.entities.DaoMaster;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.di.annotations.DatabaseInfo;
@Singleton
public class DbHelper extends DaoMaster.DevOpenHelper {
@Inject
DbHelper(@ApplicationContext Context context, @DatabaseInfo String dbName) {
super(context, dbName);
}
}

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.db.dao.entities; package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.DaoException;
import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Entity;

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.db.dao.entities; package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.DaoException;
import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Entity;

View File

@ -1,7 +1,4 @@
package io.github.wulkanowy.db.dao.entities; package io.github.wulkanowy.data.db.dao.entities;
import android.os.Parcel;
import android.os.Parcelable;
import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;
@ -11,13 +8,15 @@ import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id; import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Property; import org.greenrobot.greendao.annotation.Property;
import java.io.Serializable;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
@Entity( @Entity(
nameInDb = "Grades", nameInDb = "Grades",
active = true active = true
) )
public class Grade implements Parcelable { public class Grade implements Serializable {
@Id(autoincrement = true) @Id(autoincrement = true)
protected Long id; protected Long id;
@ -61,9 +60,7 @@ public class Grade implements Parcelable {
@Property(nameInDb = "READ") @Property(nameInDb = "READ")
private boolean read = true; private boolean read = true;
protected Grade(Parcel source) { private static final long serialVersionUID = 42L;
value = source.readString();
}
@Generated(hash = 568899968) @Generated(hash = 568899968)
public Grade(Long id, Long subjectId, Long userId, String subject, String value, public Grade(Long id, Long subjectId, Long userId, String subject, String value,
@ -90,36 +87,6 @@ public class Grade implements Parcelable {
public Grade() { public Grade() {
} }
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(subject);
parcel.writeString(value);
parcel.writeString(color);
parcel.writeString(symbol);
parcel.writeString(description);
parcel.writeString(weight);
parcel.writeString(date);
parcel.writeString(teacher);
parcel.writeString(semester);
}
public static final Creator<Grade> CREATOR = new Creator<Grade>() {
@Override
public Grade createFromParcel(Parcel source) {
return new Grade(source);
}
@Override
public Grade[] newArray(int size) {
return new Grade[size];
}
};
/** /**
* Used to resolve relations * Used to resolve relations
*/ */

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.db.dao.entities; package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.DaoException;
import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Entity;
@ -7,12 +7,14 @@ import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Index; import org.greenrobot.greendao.annotation.Index;
import org.greenrobot.greendao.annotation.Property; import org.greenrobot.greendao.annotation.Property;
import java.io.Serializable;
@Entity( @Entity(
nameInDb = "Lessons", nameInDb = "Lessons",
active = true, active = true,
indexes ={@Index(value = "dayId,date,startTime,endTime", unique = true)} indexes = {@Index(value = "dayId,date,startTime,endTime", unique = true)}
) )
public class Lesson { public class Lesson implements Serializable {
@Id(autoincrement = true) @Id(autoincrement = true)
private Long id; private Long id;
@ -65,6 +67,8 @@ public class Lesson {
@Property(nameInDb = "IS_NEW_MOVED_IN_CANCELED") @Property(nameInDb = "IS_NEW_MOVED_IN_CANCELED")
private boolean isNewMovedInOrChanged = false; private boolean isNewMovedInOrChanged = false;
private static final long serialVersionUID = 42L;
/** /**
* Used to resolve relations * Used to resolve relations
*/ */

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.db.dao.entities; package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.DaoException;
import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Entity;

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.db.dao.entities; package io.github.wulkanowy.data.db.dao.entities;
import org.greenrobot.greendao.DaoException; import org.greenrobot.greendao.DaoException;
import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Entity;

View File

@ -0,0 +1,59 @@
package io.github.wulkanowy.data.db.resources;
import android.content.Context;
import android.content.res.Resources;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.R;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.utils.AppConstant;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.security.CryptoException;
@Singleton
public class AppResources implements ResourcesContract {
private Resources resources;
@Inject
AppResources(@ApplicationContext Context context) {
resources = context.getResources();
}
@Override
public String[] getSymbolsKeysArray() {
return resources.getStringArray(R.array.symbols);
}
@Override
public String[] getSymbolsValuesArray() {
return resources.getStringArray(R.array.symbols_values);
}
@Override
public String getErrorLoginMessage(Exception exception) {
LogUtils.error(AppConstant.APP_NAME + " encountered a error", exception);
if (exception instanceof CryptoException) {
return resources.getString(R.string.encrypt_failed_text);
} else if (exception instanceof UnknownHostException) {
return resources.getString(R.string.noInternet_text);
} 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);
} else if (exception instanceof VulcanOfflineException) {
return resources.getString(R.string.error_host_offline);
} else {
return exception.getMessage();
}
}
}

View File

@ -0,0 +1,10 @@
package io.github.wulkanowy.data.db.resources;
public interface ResourcesContract {
String[] getSymbolsKeysArray();
String[] getSymbolsValuesArray();
String getErrorLoginMessage(Exception e);
}

View File

@ -0,0 +1,33 @@
package io.github.wulkanowy.data.db.shared;
import android.content.Context;
import android.content.SharedPreferences;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.di.annotations.SharedPreferencesInfo;
@Singleton
public class SharedPref implements SharedPrefContract {
private static final String SHARED_KEY_USER_ID = "USER_ID";
private final SharedPreferences sharedPreferences;
@Inject
SharedPref(@ApplicationContext Context context, @SharedPreferencesInfo String sharedName) {
sharedPreferences = context.getSharedPreferences(sharedName, Context.MODE_PRIVATE);
}
@Override
public long getCurrentUserId() {
return sharedPreferences.getLong(SHARED_KEY_USER_ID, 0);
}
@Override
public void setCurrentUserId(long userId) {
sharedPreferences.edit().putLong(SHARED_KEY_USER_ID, userId).apply();
}
}

View File

@ -0,0 +1,8 @@
package io.github.wulkanowy.data.db.shared;
public interface SharedPrefContract {
long getCurrentUserId();
void setCurrentUserId(long userId);
}

View File

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

View File

@ -0,0 +1,72 @@
package io.github.wulkanowy.data.sync.grades;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.data.db.dao.entities.Account;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.data.db.dao.entities.SubjectDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.data.sync.SyncContract;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.EntitiesCompare;
import io.github.wulkanowy.utils.LogUtils;
@Singleton
public class GradeSync implements SyncContract {
private final DaoSession daoSession;
private final Vulcan vulcan;
private final SharedPrefContract sharedPref;
@Inject
GradeSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) {
this.daoSession = daoSession;
this.sharedPref = sharedPref;
this.vulcan = vulcan;
}
@Override
public void sync() throws IOException, NotLoggedInErrorException, ParseException {
long userId = sharedPref.getCurrentUserId();
Account account = daoSession.getAccountDao().load(userId);
account.resetGradeList();
account.resetSubjectList();
List<Grade> gradesFromNet = DataObjectConverter
.gradesToGradeEntities(vulcan.getGradesList().getAll());
List<Grade> gradesFromDb = account.getGradeList();
List<Grade> updatedGrades = EntitiesCompare.compareGradeList(gradesFromNet, gradesFromDb);
daoSession.getGradeDao().deleteInTx(gradesFromDb);
List<Grade> lastList = new ArrayList<>();
for (Grade grade : updatedGrades) {
grade.setUserId(userId);
grade.setSubjectId(daoSession.getSubjectDao().queryBuilder()
.where(SubjectDao.Properties.Name.eq(grade.getSubject()),
SubjectDao.Properties.UserId.eq(userId))
.build()
.uniqueOrThrow().getId());
lastList.add(grade);
}
daoSession.getGradeDao().insertInTx(lastList);
LogUtils.debug("Synchronization grades (amount = " + lastList.size() + ")");
}
}

View File

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

View File

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

View File

@ -0,0 +1,61 @@
package io.github.wulkanowy.data.sync.subjects;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.data.db.dao.entities.SubjectDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.data.sync.SyncContract;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
@Singleton
public class SubjectSync implements SyncContract {
private final SubjectDao subjectDao;
private final Vulcan vulcan;
private final SharedPrefContract sharedPref;
@Inject
SubjectSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) {
this.subjectDao = daoSession.getSubjectDao();
this.sharedPref = sharedPref;
this.vulcan = vulcan;
}
@Override
public void sync() throws NotLoggedInErrorException, IOException, ParseException {
long userId = sharedPref.getCurrentUserId();
List<Subject> subjectsFromNet = DataObjectConverter
.subjectsToSubjectEntities(vulcan.getSubjectsList().getAll());
subjectDao.deleteInTx(subjectDao.queryBuilder()
.where(SubjectDao.Properties.UserId.eq(userId))
.build()
.list());
List<Subject> lastList = new ArrayList<>();
for (Subject subject : subjectsFromNet) {
subject.setUserId(userId);
lastList.add(subject);
}
subjectDao.insertInTx(lastList);
LogUtils.debug("Synchronization subjects (amount = " + lastList.size() + ")");
}
}

View File

@ -0,0 +1,119 @@
package io.github.wulkanowy.data.sync.timetable;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.data.db.dao.entities.DaoSession;
import io.github.wulkanowy.data.db.dao.entities.Day;
import io.github.wulkanowy.data.db.dao.entities.DayDao;
import io.github.wulkanowy.data.db.dao.entities.Lesson;
import io.github.wulkanowy.data.db.dao.entities.LessonDao;
import io.github.wulkanowy.data.db.dao.entities.Week;
import io.github.wulkanowy.data.db.dao.entities.WeekDao;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.LogUtils;
import io.github.wulkanowy.utils.TimeUtils;
@Singleton
public class TimetableSync implements TimetableSyncContract {
private final DaoSession daoSession;
private final Vulcan vulcan;
private final SharedPrefContract sharedPref;
@Inject
TimetableSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) {
this.daoSession = daoSession;
this.sharedPref = sharedPref;
this.vulcan = vulcan;
}
@Override
public void syncTimetable(String date) throws NotLoggedInErrorException, IOException, ParseException {
long userId = sharedPref.getCurrentUserId();
io.github.wulkanowy.api.timetable.Week weekFromNet = date == null ? vulcan.getTimetable().getWeekTable()
: vulcan.getTimetable().getWeekTable(String.valueOf(TimeUtils.getNetTicks(date)));
Week weekFromDb = daoSession.getWeekDao().queryBuilder()
.where(WeekDao.Properties.UserId.eq(userId),
WeekDao.Properties.StartDayDate.eq(weekFromNet.getStartDayDate()))
.unique();
Long weekId;
if (weekFromDb == null) {
Week weekFromNetEntity = DataObjectConverter.weekToWeekEntity(weekFromNet).setUserId(userId);
weekId = daoSession.getWeekDao().insert(weekFromNetEntity);
} else {
weekId = weekFromDb.getId();
}
List<io.github.wulkanowy.api.timetable.Day> dayListFromNet = weekFromNet.getDays();
List<Lesson> updatedLessonList = new ArrayList<>();
for (io.github.wulkanowy.api.timetable.Day dayFromNet : dayListFromNet) {
Day dayFromNetEntity = DataObjectConverter.dayToDayEntity(dayFromNet);
Day dayFromDb = daoSession.getDayDao().queryBuilder()
.where(DayDao.Properties.UserId.eq(userId),
DayDao.Properties.WeekId.eq(weekId),
DayDao.Properties.Date.eq(dayFromNetEntity.getDate()))
.unique();
dayFromNetEntity.setUserId(userId);
dayFromNetEntity.setWeekId(weekId);
Long dayId;
if (dayFromDb != null) {
dayFromNetEntity.setId(dayFromDb.getId());
daoSession.getDayDao().save(dayFromNetEntity);
dayId = dayFromNetEntity.getId();
} else {
dayId = daoSession.getDayDao().insert(dayFromNetEntity);
}
List<Lesson> lessonListFromNetEntities = DataObjectConverter
.lessonsToLessonsEntities(dayFromNet.getLessons());
for (Lesson lessonFromNetEntity : lessonListFromNetEntities) {
Lesson lessonFromDb = daoSession.getLessonDao().queryBuilder()
.where(LessonDao.Properties.DayId.eq(dayId),
LessonDao.Properties.Date.eq(lessonFromNetEntity.getDate()),
LessonDao.Properties.StartTime.eq(lessonFromNetEntity.getStartTime()),
LessonDao.Properties.EndTime.eq(lessonFromNetEntity.getEndTime()))
.unique();
if (lessonFromDb != null) {
lessonFromNetEntity.setId(lessonFromDb.getId());
}
lessonFromNetEntity.setDayId(dayFromNetEntity.getId());
if (!"".equals(lessonFromNetEntity.getSubject())) {
updatedLessonList.add(lessonFromNetEntity);
}
}
}
daoSession.getLessonDao().saveInTx(updatedLessonList);
LogUtils.debug("Synchronization lessons (amount = " + updatedLessonList.size() + ")");
}
@Override
public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException {
syncTimetable(null);
}
}

View File

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

View File

@ -1,20 +0,0 @@
package io.github.wulkanowy.db.dao;
import org.greenrobot.greendao.query.Query;
import java.util.List;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import io.github.wulkanowy.db.dao.entities.Grade;
import io.github.wulkanowy.db.dao.entities.GradeDao;
public class DatabaseAccess {
public List<Grade> getNewGrades(DaoSession daoSession) {
Query<Grade> gradeQuery = daoSession.getGradeDao().queryBuilder()
.where(GradeDao.Properties.IsNew.eq(1))
.build();
return gradeQuery.list();
}
}

View File

@ -0,0 +1,11 @@
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

@ -0,0 +1,11 @@
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

@ -0,0 +1,11 @@
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 DatabaseInfo {
}

View File

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

View File

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

View File

@ -0,0 +1,11 @@
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

@ -0,0 +1,11 @@
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 SyncGrades {
}

View File

@ -0,0 +1,11 @@
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 SyncSubjects {
}

View File

@ -0,0 +1,19 @@
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

@ -0,0 +1,26 @@
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.SyncJob;
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
@ApplicationContext
Context getContext();
RepositoryContract getRepository();
void inject(WulkanowyApp wulkanowyApp);
void inject(SyncJob syncJob);
}

View File

@ -0,0 +1,25 @@
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.dashboard.DashboardFragment;
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(DashboardFragment dashboardFragment);
void inject(TimetableFragment timetableFragment);
void inject(TimetableTabFragment timetableTabFragment);
}

View File

@ -0,0 +1,64 @@
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.login.LoginContract;
import io.github.wulkanowy.ui.login.LoginPresenter;
import io.github.wulkanowy.ui.main.MainContract;
import io.github.wulkanowy.ui.main.MainPagerAdapter;
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
MainPagerAdapter provideMainPagerAdapter() {
return new MainPagerAdapter(activity.getSupportFragmentManager());
}
}

View File

@ -3,13 +3,41 @@ package io.github.wulkanowy.di.modules;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import com.firebase.jobdispatcher.FirebaseJobDispatcher;
import com.firebase.jobdispatcher.GooglePlayDriver;
import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
import dagger.Provides; 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.DbHelper;
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.AppResources;
import io.github.wulkanowy.data.db.resources.ResourcesContract;
import io.github.wulkanowy.data.db.shared.SharedPref;
import io.github.wulkanowy.data.db.shared.SharedPrefContract;
import io.github.wulkanowy.data.sync.SyncContract;
import io.github.wulkanowy.data.sync.grades.GradeSync;
import io.github.wulkanowy.data.sync.login.LoginSync;
import io.github.wulkanowy.data.sync.login.LoginSyncContract;
import io.github.wulkanowy.data.sync.subjects.SubjectSync;
import io.github.wulkanowy.data.sync.timetable.TimetableSync;
import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract;
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.di.annotations.SyncGrades;
import io.github.wulkanowy.di.annotations.SyncSubjects;
import io.github.wulkanowy.utils.AppConstant;
@Module @Module
public class ApplicationModule { public class ApplicationModule {
protected final Application application; private final Application application;
public ApplicationModule(Application application) { public ApplicationModule(Application application) {
this.application = application; this.application = application;
@ -20,8 +48,82 @@ public class ApplicationModule {
return application; return application;
} }
@ApplicationContext
@Provides @Provides
Context provideAppContext() { Context provideAppContext() {
return application; 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(SharedPref sharedPref) {
return sharedPref;
}
@Singleton
@Provides
ResourcesContract provideAppResources(AppResources appResources) {
return appResources;
}
@Singleton
@Provides
LoginSyncContract provideLoginSync(LoginSync loginSync) {
return loginSync;
}
@SyncGrades
@Singleton
@Provides
SyncContract provideGradesSync(GradeSync gradeSync) {
return gradeSync;
}
@SyncSubjects
@Singleton
@Provides
SyncContract provideSubjectSync(SubjectSync subjectSync) {
return subjectSync;
}
@Singleton
@Provides
TimetableSyncContract provideTimetableSync(TimetableSync timetableSync) {
return timetableSync;
}
@Provides
FirebaseJobDispatcher provideDispatcher() {
return new FirebaseJobDispatcher(new GooglePlayDriver(application));
}
} }

View File

@ -0,0 +1,76 @@
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.main.attendance.AttendanceContract;
import io.github.wulkanowy.ui.main.attendance.AttendancePresenter;
import io.github.wulkanowy.ui.main.dashboard.DashboardContract;
import io.github.wulkanowy.ui.main.dashboard.DashboardPresenter;
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.TimetablePagerAdapter;
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
DashboardContract.Presenter provideDashboardPresenter(DashboardPresenter dashboardPresenter) {
return dashboardPresenter;
}
@PerFragment
@Provides
TimetableContract.Presenter provideTimetablePresenter(TimetablePresenter timetablePresenter) {
return timetablePresenter;
}
@PerFragment
@Provides
TimetableTabContract.Presenter provideTimetableTabPresenter(TimetableTabPresenter timetableTabPresenter) {
return timetableTabPresenter;
}
@Provides
TimetablePagerAdapter provideTimetablePagerAdapter() {
return new TimetablePagerAdapter(fragment.getChildFragmentManager());
}
@Provides
FlexibleAdapter<GradeHeaderItem> provideGradesAdapter() {
return new FlexibleAdapter<>(null);
}
@Provides
FlexibleAdapter<TimetableHeaderItem> provideTimetableTabAdapter() {
return new FlexibleAdapter<>(null);
}
}

View File

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

View File

@ -0,0 +1,114 @@
package io.github.wulkanowy.services;
import android.app.PendingIntent;
import android.content.Context;
import android.support.v4.app.NotificationCompat;
import com.firebase.jobdispatcher.Constraint;
import com.firebase.jobdispatcher.FirebaseJobDispatcher;
import com.firebase.jobdispatcher.GooglePlayDriver;
import com.firebase.jobdispatcher.JobParameters;
import com.firebase.jobdispatcher.JobService;
import com.firebase.jobdispatcher.Lifetime;
import com.firebase.jobdispatcher.RetryStrategy;
import com.firebase.jobdispatcher.SimpleJobService;
import com.firebase.jobdispatcher.Trigger;
import java.util.List;
import javax.inject.Inject;
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.ui.main.MainActivity;
import io.github.wulkanowy.utils.LogUtils;
public class SyncJob extends SimpleJobService {
private static final int DEFAULT_INTERVAL_START = 60 * 50;
private static final int DEFAULT_INTERVAL_END = DEFAULT_INTERVAL_START + (60 * 40);
public static final String EXTRA_INTENT_KEY = "cardId";
private List<Grade> gradeList;
@Inject
RepositoryContract repository;
public static void start(Context context) {
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
dispatcher.mustSchedule(dispatcher.newJobBuilder()
.setLifetime(Lifetime.FOREVER)
.setService(SyncJob.class)
.setTag("SyncJob")
.setRecurring(true)
.setTrigger(Trigger.executionWindow(DEFAULT_INTERVAL_START, DEFAULT_INTERVAL_END))
.setConstraints(Constraint.ON_ANY_NETWORK)
.setReplaceCurrent(false)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.build());
}
@Override
public void onCreate() {
super.onCreate();
((WulkanowyApp) getApplication()).getApplicationComponent().inject(this);
}
@Override
public int onRunJob(JobParameters job) {
try {
repository.loginCurrentUser();
repository.syncAll();
gradeList = repository.getNewGrades();
if (!gradeList.isEmpty()) {
showNotification();
}
return JobService.RESULT_SUCCESS;
} catch (Exception e) {
LogUtils.error("During background synchronization an error occurred", e);
return JobService.RESULT_FAIL_RETRY;
}
}
private void showNotification() {
NotificationService service = new NotificationService(getApplicationContext());
service.notify(service.notificationBuilder()
.setContentTitle(getStringTitle())
.setContentText(getStringContent())
.setSmallIcon(R.drawable.ic_notification)
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setColor(getResources().getColor(R.color.colorPrimary))
.setContentIntent(PendingIntent.getActivity(getApplicationContext(), 0,
MainActivity.getStartIntent(getApplicationContext()).putExtra(EXTRA_INTENT_KEY, 0)
, 0
))
.build());
}
private String getStringTitle() {
if (gradeList.size() == 1) {
return getResources().getQuantityString(R.plurals.newGradePlurals, 1);
} else {
return getResources().getQuantityString(R.plurals.newGradePlurals, 2);
}
}
private String getStringContent() {
if (gradeList.size() == 1) {
return gradeList.get(0).getSubject();
} else {
return getResources().getQuantityString(R.plurals.receivedNewGradePlurals,
gradeList.size(), gradeList.size());
}
}
}

View File

@ -1,189 +0,0 @@
package io.github.wulkanowy.services;
import android.Manifest;
import android.app.Activity;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import com.github.javiersantos.appupdater.AppUpdaterUtils;
import com.github.javiersantos.appupdater.enums.AppUpdaterError;
import com.github.javiersantos.appupdater.enums.UpdateFrom;
import com.github.javiersantos.appupdater.objects.Update;
import java.io.File;
import io.github.wulkanowy.BuildConfig;
import io.github.wulkanowy.R;
public class Updater {
private static final String DEBUG_TAG = "WulkanowyUpdater";
private static final int PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 0;
private Activity activity;
private Update update;
private DownloadManager downloadManager;
private BroadcastReceiver onComplete = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
DownloadManager.Query query = new DownloadManager.Query();
Cursor c = downloadManager.query(query);
if (c.moveToFirst()) {
String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
if (uriString.substring(0, 7).matches("file://")) {
uriString = uriString.substring(7);
}
File file = new File(uriString);
Intent install;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
install = new Intent(Intent.ACTION_INSTALL_PACKAGE);
install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
install.setData(FileProvider.getUriForFile(context,
BuildConfig.APPLICATION_ID + ".fileprovider", file));
} else {
install = new Intent(Intent.ACTION_VIEW);
install.setDataAndType(Uri.parse("file://" + file.getAbsolutePath()),
"application/vnd.android.package-archive");
}
context.startActivity(install);
}
}
};
public Updater(Activity activity) {
this.activity = activity;
downloadManager = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
activity.registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
private void downloadUpdate() {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
startDownload();
} else {
requestWriteStoragePermission();
}
}
private void requestWriteStoragePermission() {
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE);
}
private void startDownload() {
Snackbar.make(activity.findViewById(R.id.fragment_container), "Downloading started.", Snackbar.LENGTH_SHORT).show();
String path = Environment.getExternalStorageDirectory().toString() + File.separator +
Environment.DIRECTORY_DOWNLOADS + File.separator + "wulkanowy";
File dir = new File(path);
if(!dir.mkdirs()) {
for (String aChildren : dir.list()) {
new File(dir, aChildren).delete();
}
}
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(update.getUrlToDownload().toString()))
.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE)
.setAllowedOverRoaming(false)
.setTitle("Wulkanowy v" + update.getLatestVersionCode())
.setDescription(update.getLatestVersion())
.setVisibleInDownloadsUi(true)
.setMimeType("application/vnd.android.package-archive")
.setDestinationUri(Uri.fromFile(new File(path + File.separator + update.getLatestVersion() + ".apk")));
downloadManager.enqueue(request);
}
public void onRequestPermissionsResult(int requestCode, @NonNull int[] grantResults) {
if (requestCode == PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE) {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
downloadUpdate();
} else {
Snackbar.make(activity.findViewById(R.id.fragment_container),
"Write storage permission request was denied.",
Snackbar.LENGTH_LONG)
.show();
}
}
}
public Updater checkForUpdates() {
AppUpdaterUtils appUpdaterUtils = new AppUpdaterUtils(activity)
.setUpdateFrom(UpdateFrom.JSON)
.setUpdateJSON(BuildConfig.UPDATE_URL)
.withListener(new AppUpdaterUtils.UpdateListener() {
@Override
public void onSuccess(final Update currentUpdate, Boolean isUpdateAvailable) {
Log.d(DEBUG_TAG, "Latest Version: " + currentUpdate.getLatestVersion());
Log.d(DEBUG_TAG, "Latest Version Code: " + currentUpdate.getLatestVersionCode().toString());
Log.d(DEBUG_TAG, "URL: " + currentUpdate.getUrlToDownload().toString());
Log.d(DEBUG_TAG, "Is update available?: " + Boolean.toString(isUpdateAvailable));
update = currentUpdate;
showDialog(isUpdateAvailable);
}
@Override
public void onFailed(AppUpdaterError error) {
Log.e(DEBUG_TAG, "Something went wrong");
Log.e(DEBUG_TAG, error.toString());
}
});
appUpdaterUtils.start();
return this;
}
private void showDialog(boolean isUpdateAvailable) {
if (isUpdateAvailable) {
new AlertDialog.Builder(activity)
.setTitle("Update is available")
.setMessage("Update to version " + update.getLatestVersionCode().toString() +
" is available. Your version is " + BuildConfig.VERSION_CODE + ". Update?")
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
downloadUpdate();
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.show();
}
}
public void onDestroy(Activity activity) {
activity.unregisterReceiver(onComplete);
}
}

View File

@ -1,80 +0,0 @@
package io.github.wulkanowy.services.jobs;
import android.app.PendingIntent;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import com.firebase.jobdispatcher.Constraint;
import com.firebase.jobdispatcher.FirebaseJobDispatcher;
import com.firebase.jobdispatcher.Job;
import com.firebase.jobdispatcher.Lifetime;
import com.firebase.jobdispatcher.RetryStrategy;
import com.firebase.jobdispatcher.Trigger;
import java.util.List;
import java.util.Random;
import io.github.wulkanowy.R;
import io.github.wulkanowy.WulkanowyApp;
import io.github.wulkanowy.db.dao.DatabaseAccess;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import io.github.wulkanowy.db.dao.entities.Grade;
import io.github.wulkanowy.services.notifications.NotificationBuilder;
import io.github.wulkanowy.services.sync.VulcanSync;
import io.github.wulkanowy.ui.main.DashboardActivity;
public class FullSyncJob extends VulcanJobHelper {
private static final String UNIQUE_TAG = "FullSync";
private static final int DEFAULT_INTERVAL_START = 60 * 50;
private static final int DEFAULT_INTERVAL_END = DEFAULT_INTERVAL_START + (60 * 40);
@Override
protected Job createJob(FirebaseJobDispatcher dispatcher) {
return dispatcher.newJobBuilder()
.setLifetime(Lifetime.FOREVER)
.setService(SyncService.class)
.setTag(UNIQUE_TAG)
.setRecurring(true)
.setTrigger(Trigger.executionWindow(DEFAULT_INTERVAL_START, DEFAULT_INTERVAL_END))
.setConstraints(Constraint.ON_ANY_NETWORK)
.setReplaceCurrent(false)
.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
.build();
}
public static class SyncService extends VulcanService {
@Override
public void workToBePerformed() throws Exception {
DaoSession daoSession = ((WulkanowyApp) getApplication()).getDaoSession();
VulcanSync synchronization = new VulcanSync()
.loginCurrentUser(getApplicationContext(), daoSession);
synchronization.syncAll();
List<Grade> newGradeList = new DatabaseAccess().getNewGrades(daoSession);
if (newGradeList.size() == 1) {
buildNotify(getResources().getQuantityString(R.plurals.newGradePlurals, 1),
newGradeList.get(0).getSubject());
} else if (newGradeList.size() > 1) {
buildNotify(getResources().getQuantityString(R.plurals.newGradePlurals, 2),
getResources().getQuantityString(R.plurals.receivedNewGradePlurals, newGradeList.size(), newGradeList.size()));
}
}
private void buildNotify(String title, String bodyText) {
Intent intent = new Intent(getApplicationContext(), DashboardActivity.class);
intent.putExtra("cardID", 1);
PendingIntent pendingIntent = PendingIntent
.getActivity(getApplicationContext(), 0, intent, 0);
NotificationBuilder notificationBuilder = new NotificationBuilder(getApplicationContext());
NotificationCompat.Builder builder = notificationBuilder
.getNotifications(title, bodyText, pendingIntent);
notificationBuilder.getManager().notify(new Random().nextInt(10000), builder.build());
}
}
}

View File

@ -1,22 +0,0 @@
package io.github.wulkanowy.services.jobs;
import android.content.Context;
import android.util.Log;
import com.firebase.jobdispatcher.FirebaseJobDispatcher;
import com.firebase.jobdispatcher.GooglePlayDriver;
import com.firebase.jobdispatcher.Job;
public abstract class VulcanJobHelper {
public static final String DEBUG_TAG = "SynchronizationService";
public final void scheduledJob(Context context) {
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
dispatcher.mustSchedule(createJob(dispatcher));
Log.i(DEBUG_TAG, "Wulkanowy Job is initiation: " + this.toString());
}
protected abstract Job createJob(FirebaseJobDispatcher dispatcher);
}

View File

@ -1,57 +0,0 @@
package io.github.wulkanowy.services.jobs;
import android.os.AsyncTask;
import android.util.Log;
import com.firebase.jobdispatcher.JobParameters;
import com.firebase.jobdispatcher.JobService;
import java.lang.ref.WeakReference;
public abstract class VulcanService extends JobService {
private SyncTask syncTask;
@Override
public boolean onStartJob(JobParameters params) {
Log.d(VulcanJobHelper.DEBUG_TAG, "Wulkanowy services start");
syncTask = new SyncTask(this, params);
syncTask.execute();
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
Log.e(VulcanJobHelper.DEBUG_TAG, "Wulkanowy services stop");
if (syncTask != null) {
syncTask.cancel(true);
}
return true;
}
public abstract void workToBePerformed() throws Exception;
private static class SyncTask extends AsyncTask<Void, Void, Void> {
private JobParameters jobParameters;
private WeakReference<VulcanService> vulcanService;
public SyncTask(VulcanService vulcanService, JobParameters jobParameters) {
this.jobParameters = jobParameters;
this.vulcanService = new WeakReference<>(vulcanService);
}
@Override
protected Void doInBackground(Void... voids) {
try {
vulcanService.get().workToBePerformed();
} catch (Exception e) {
Log.e(VulcanJobHelper.DEBUG_TAG, "User logging in the background failed", e);
} finally {
vulcanService.get().jobFinished(jobParameters, false);
}
return null;
}
}
}

View File

@ -1,60 +0,0 @@
package io.github.wulkanowy.services.notifications;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import io.github.wulkanowy.R;
public class NotificationBuilder extends ContextWrapper {
public static final String CHANNEL_ID = "Wulkanowy_New_Grade_Channel";
public static final String CHANNEL_NAME = "New Grade Channel";
private NotificationManager manager;
public NotificationBuilder(Context context) {
super(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannel();
}
}
@TargetApi(26)
private void createChannel() {
NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH);
notificationChannel.enableLights(true);
notificationChannel.enableVibration(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
getManager().createNotificationChannel(notificationChannel);
}
public NotificationCompat.Builder getNotifications(String title, String bodyText, PendingIntent pendingIntent) {
return new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
.setContentTitle(title)
.setContentText(bodyText)
.setSmallIcon(R.drawable.ic_notification)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setChannelId(CHANNEL_ID)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setColor(getResources().getColor(R.color.colorPrimary));
}
public NotificationManager getManager() {
if (manager == null) {
manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
return manager;
}
}

View File

@ -1,64 +0,0 @@
package io.github.wulkanowy.services.sync;
import android.content.Context;
import android.util.Log;
import java.io.IOException;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.login.AccountPermissionException;
import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.api.login.LoginErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException;
import io.github.wulkanowy.db.dao.entities.Account;
import io.github.wulkanowy.db.dao.entities.AccountDao;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import io.github.wulkanowy.services.jobs.VulcanJobHelper;
import io.github.wulkanowy.utils.security.CryptoException;
import io.github.wulkanowy.utils.security.Safety;
public class CurrentAccountLogin {
private final Context context;
private final DaoSession daoSession;
private final Vulcan vulcan;
public CurrentAccountLogin(Context context, DaoSession daoSession, Vulcan vulcan) {
this.context = context;
this.daoSession = daoSession;
this.vulcan = vulcan;
}
public LoginSession loginCurrentUser() throws CryptoException,
BadCredentialsException, AccountPermissionException, IOException, LoginErrorException, VulcanOfflineException {
AccountDao accountDao = daoSession.getAccountDao();
long userId = context.getSharedPreferences("LoginData", Context.MODE_PRIVATE).getLong("userId", 0);
if (userId != 0) {
Log.d(VulcanJobHelper.DEBUG_TAG, "Login current user id=" + String.valueOf(userId));
Safety safety = new Safety();
Account account = accountDao.load(userId);
vulcan.login(
account.getEmail(),
safety.decrypt(account.getEmail(), account.getPassword()),
account.getSymbol(),
account.getSnpId()
);
return new LoginSession()
.setDaoSession(daoSession)
.setUserId(userId)
.setVulcan(vulcan);
} else {
Log.wtf(VulcanJobHelper.DEBUG_TAG, "loginCurrentUser - USERID IS EMPTY");
throw new IOException("Can't find user with index 0");
}
}
}

View File

@ -1,59 +0,0 @@
package io.github.wulkanowy.services.sync;
import android.content.Context;
import android.content.SharedPreferences;
import java.io.IOException;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.login.AccountPermissionException;
import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException;
import io.github.wulkanowy.db.dao.entities.Account;
import io.github.wulkanowy.db.dao.entities.AccountDao;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import io.github.wulkanowy.utils.security.CryptoException;
import io.github.wulkanowy.utils.security.Safety;
public class FirstAccountLogin {
private final Context context;
private final DaoSession daoSession;
private final Vulcan vulcan;
public FirstAccountLogin(Context context, DaoSession daoSession, Vulcan vulcan) {
this.context = context;
this.daoSession = daoSession;
this.vulcan = vulcan;
}
public LoginSession login(String email, String password, String symbol)
throws NotLoggedInErrorException, AccountPermissionException, IOException, CryptoException, VulcanOfflineException, BadCredentialsException {
String realSymbol = vulcan.login(email, password, symbol);
AccountDao accountDao = daoSession.getAccountDao();
Safety safety = new Safety();
Account account = new Account()
.setName(vulcan.getBasicInformation().getPersonalData().getFirstAndLastName())
.setEmail(email)
.setPassword(safety.encrypt(email, password, context))
.setSymbol(realSymbol)
.setSnpId(vulcan.getStudentAndParent().getId());
long userId = accountDao.insert(account);
SharedPreferences sharedPreferences = context.getSharedPreferences("LoginData", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putLong("userId", userId);
editor.apply();
return new LoginSession()
.setVulcan(vulcan)
.setUserId(userId)
.setDaoSession(daoSession);
}
}

View File

@ -1,64 +0,0 @@
package io.github.wulkanowy.services.sync;
import android.util.Log;
import org.greenrobot.greendao.query.Query;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.grades.GradesList;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.db.dao.entities.Account;
import io.github.wulkanowy.db.dao.entities.AccountDao;
import io.github.wulkanowy.db.dao.entities.Grade;
import io.github.wulkanowy.db.dao.entities.GradeDao;
import io.github.wulkanowy.db.dao.entities.Subject;
import io.github.wulkanowy.db.dao.entities.SubjectDao;
import io.github.wulkanowy.services.jobs.VulcanJobHelper;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.EntitiesCompare;
public class GradesSync {
public void sync(LoginSession loginSession) throws IOException,
ParseException, NotLoggedInErrorException {
GradesList gradesList = loginSession.getVulcan().getGradesList();
GradeDao gradeDao = loginSession.getDaoSession().getGradeDao();
AccountDao accountDao = loginSession.getDaoSession().getAccountDao();
SubjectDao subjectDao = loginSession.getDaoSession().getSubjectDao();
Account account = accountDao.load(loginSession.getUserId());
account.resetGradeList();
account.resetSubjectList();
List<Grade> gradesFromDb = account.getGradeList();
List<Grade> gradeEntitiesList = DataObjectConverter.gradesToGradeEntities(gradesList.getAll());
List<Grade> updatedList = EntitiesCompare.compareGradeList(gradeEntitiesList, gradesFromDb);
List<Grade> lastList = new ArrayList<>();
GradeDao.dropTable(gradeDao.getDatabase(), true);
GradeDao.createTable(gradeDao.getDatabase(), false);
for (Grade grade : updatedList) {
Query<Subject> subjectQuery = subjectDao.queryBuilder()
.where(SubjectDao.Properties.Name.eq(grade.getSubject()))
.build();
grade.setUserId(loginSession.getUserId());
grade.setSubjectId((subjectQuery.uniqueOrThrow()).getId());
lastList.add(grade);
}
gradeDao.insertInTx(lastList);
Log.d(VulcanJobHelper.DEBUG_TAG, "Synchronization grades (amount = " + String.valueOf(lastList.size() + ")"));
}
}

View File

@ -1,69 +0,0 @@
package io.github.wulkanowy.services.sync;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.db.dao.entities.DaoSession;
public class LoginSession {
private Long userId;
private Vulcan vulcan;
private DaoSession daoSession;
public Long getUserId() {
return userId;
}
public LoginSession setUserId(Long userId) {
this.userId = userId;
return this;
}
public Vulcan getVulcan() {
return vulcan;
}
public LoginSession setVulcan(Vulcan vulcan) {
this.vulcan = vulcan;
return this;
}
public DaoSession getDaoSession() {
return daoSession;
}
public LoginSession setDaoSession(DaoSession daoSession) {
this.daoSession = daoSession;
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LoginSession that = (LoginSession) o;
return new EqualsBuilder()
.append(userId, that.userId)
.append(vulcan, that.vulcan)
.append(daoSession, that.daoSession)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(userId)
.append(vulcan)
.append(daoSession)
.toHashCode();
}
}

View File

@ -1,39 +0,0 @@
package io.github.wulkanowy.services.sync;
import android.util.Log;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.grades.SubjectsList;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.db.dao.entities.Subject;
import io.github.wulkanowy.db.dao.entities.SubjectDao;
import io.github.wulkanowy.services.jobs.VulcanJobHelper;
import io.github.wulkanowy.utils.DataObjectConverter;
public class SubjectsSync {
public void sync(LoginSession loginSession) throws IOException,
NotLoggedInErrorException {
SubjectsList subjectsList = loginSession.getVulcan().getSubjectsList();
SubjectDao subjectDao = loginSession.getDaoSession().getSubjectDao();
List<Subject> subjectEntitiesList = DataObjectConverter.subjectsToSubjectEntities(subjectsList.getAll());
List<Subject> preparedList = new ArrayList<>();
for (Subject subject : subjectEntitiesList) {
subject.setUserId(loginSession.getUserId());
preparedList.add(subject);
}
SubjectDao.dropTable(subjectDao.getDatabase(), true);
SubjectDao.createTable(subjectDao.getDatabase(), false);
subjectDao.insertInTx(preparedList);
Log.d(VulcanJobHelper.DEBUG_TAG, "Synchronization subjects (amount = " + String.valueOf(subjectEntitiesList.size() + ")"));
}
}

View File

@ -1,121 +0,0 @@
package io.github.wulkanowy.services.sync;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import org.greenrobot.greendao.query.Query;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.timetable.Day;
import io.github.wulkanowy.api.timetable.Week;
import io.github.wulkanowy.db.dao.entities.DayDao;
import io.github.wulkanowy.db.dao.entities.Lesson;
import io.github.wulkanowy.db.dao.entities.LessonDao;
import io.github.wulkanowy.db.dao.entities.WeekDao;
import io.github.wulkanowy.services.jobs.VulcanJobHelper;
import io.github.wulkanowy.utils.DataObjectConverter;
import io.github.wulkanowy.utils.TimeUtils;
public class TimetableSync {
public void sync(@NonNull LoginSession loginSession, @Nullable String dateOfMonday) throws NotLoggedInErrorException,
IOException, ParseException {
DayDao dayDao = loginSession.getDaoSession().getDayDao();
LessonDao lessonDao = loginSession.getDaoSession().getLessonDao();
WeekDao weekDao = loginSession.getDaoSession().getWeekDao();
Long weekId;
Week week = dateOfMonday == null ? loginSession.getVulcan().getTimetable().getWeekTable()
: loginSession.getVulcan().getTimetable()
.getWeekTable(String.valueOf(TimeUtils.getNetTicks(dateOfMonday, "yyyy-MM-dd")));
Query<io.github.wulkanowy.db.dao.entities.Week> weekQuery = weekDao.queryBuilder()
.where(WeekDao.Properties.UserId.eq(loginSession.getUserId()), WeekDao.Properties.StartDayDate.eq(week.getStartDayDate())).build();
io.github.wulkanowy.db.dao.entities.Week week1 = weekQuery.unique();
if (week1 != null) {
weekId = week1.getId();
} else {
weekId = weekDao.insert(DataObjectConverter.weekToWeekEntitie(week).setUserId(loginSession.getUserId()));
}
List<Day> dayList = week.getDays();
dayDao.saveInTx(getPreparedDaysList(dayList, loginSession.getUserId(), weekId, dayDao));
Log.d(VulcanJobHelper.DEBUG_TAG, "Synchronization days (amount = " + dayList.size() + ")");
List<Lesson> lessonList = new ArrayList<>();
lessonList.addAll(getPreparedLessonsList(dayList, dayDao, lessonDao, loginSession.getUserId(), weekId));
lessonDao.saveInTx(lessonList);
Log.d(VulcanJobHelper.DEBUG_TAG, "Synchronization lessons (amount = " + lessonList.size() + ")");
}
private List<Lesson> getPreparedLessonsList(List<Day> dayList, DayDao dayDao, LessonDao lessonDao, long userId, long weekId) {
List<Lesson> allLessonsList = new ArrayList<>();
for (Day day : dayList) {
Query<io.github.wulkanowy.db.dao.entities.Day> dayQuery = dayDao.queryBuilder()
.where(DayDao.Properties.Date.eq(day.getDate()),
DayDao.Properties.UserId.eq(userId),
DayDao.Properties.WeekId.eq(weekId))
.build();
List<Lesson> lessonEntityList = DataObjectConverter.lessonsToLessonsEntities(day.getLessons());
List<Lesson> updatedLessonEntityList = new ArrayList<>();
for (Lesson lesson : lessonEntityList) {
Lesson lesson1 = lessonDao.queryBuilder()
.where(LessonDao.Properties.DayId.eq(dayQuery.uniqueOrThrow().getId()),
LessonDao.Properties.Date.eq(lesson.getDate()),
LessonDao.Properties.StartTime.eq(lesson.getStartTime()),
LessonDao.Properties.EndTime.eq(lesson.getEndTime()))
.unique();
if (lesson1 != null) {
lesson.setId(lesson1.getId());
}
lesson.setDayId(dayQuery.uniqueOrThrow().getId());
if (!"".equals(lesson.getSubject())) {
updatedLessonEntityList.add(lesson);
}
}
allLessonsList.addAll(updatedLessonEntityList);
}
return allLessonsList;
}
private List<io.github.wulkanowy.db.dao.entities.Day> getPreparedDaysList(List<Day> dayList, long userId, long weekId, DayDao dayDao) {
List<io.github.wulkanowy.db.dao.entities.Day> updatedDayList = new ArrayList<>();
List<io.github.wulkanowy.db.dao.entities.Day> dayEntityList = DataObjectConverter
.daysToDaysEntities(dayList);
for (io.github.wulkanowy.db.dao.entities.Day day : dayEntityList) {
Query<io.github.wulkanowy.db.dao.entities.Day> dayQuery = dayDao.queryBuilder().where(DayDao.Properties.UserId.eq(userId), DayDao.Properties.WeekId.eq(weekId), DayDao.Properties.Date.eq(day.getDate())).build();
io.github.wulkanowy.db.dao.entities.Day day1 = dayQuery.unique();
if (day1 != null) {
day.setId(day1.getId());
}
day.setUserId(userId);
day.setWeekId(weekId);
updatedDayList.add(day);
}
return updatedDayList;
}
}

View File

@ -1,105 +0,0 @@
package io.github.wulkanowy.services.sync;
import android.content.Context;
import android.support.annotation.Nullable;
import android.util.Log;
import java.io.IOException;
import io.github.wulkanowy.api.Vulcan;
import io.github.wulkanowy.api.login.AccountPermissionException;
import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.api.login.LoginErrorException;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import io.github.wulkanowy.services.jobs.VulcanJobHelper;
import io.github.wulkanowy.utils.security.CryptoException;
public class VulcanSync {
private LoginSession loginSession;
public VulcanSync(LoginSession loginSession) {
this.loginSession = loginSession;
}
public VulcanSync() {
this.loginSession = new LoginSession();
}
public void firstLoginSignInStep(Context context, DaoSession daoSession, String email, String password, String symbol)
throws NotLoggedInErrorException, AccountPermissionException, IOException, CryptoException, VulcanOfflineException, BadCredentialsException {
FirstAccountLogin firstAccountLogin = new FirstAccountLogin(context, daoSession, new Vulcan());
loginSession = firstAccountLogin.login(email, password, symbol);
}
public VulcanSync loginCurrentUser(Context context, DaoSession daoSession) throws CryptoException,
BadCredentialsException, AccountPermissionException, LoginErrorException, IOException, VulcanOfflineException {
return loginCurrentUser(context, daoSession, new Vulcan());
}
public VulcanSync loginCurrentUser(Context context, DaoSession daoSession, Vulcan vulcan)
throws CryptoException, BadCredentialsException, AccountPermissionException,
LoginErrorException, IOException, VulcanOfflineException {
CurrentAccountLogin currentAccountLogin = new CurrentAccountLogin(context, daoSession, vulcan);
loginSession = currentAccountLogin.loginCurrentUser();
return this;
}
public void syncAll() throws IOException {
syncSubjectsAndGrades();
syncTimetable();
}
public void syncGrades() throws IOException {
if (loginSession != null) {
GradesSync gradesSync = new GradesSync();
try {
gradesSync.sync(loginSession);
} catch (Exception e) {
Log.e(VulcanJobHelper.DEBUG_TAG, "Synchronisation of grades failed", e);
throw new IOException(e.getCause());
}
} else {
Log.e(VulcanJobHelper.DEBUG_TAG, "Before sync, should login user to log",
new UnsupportedOperationException());
}
}
public void syncSubjectsAndGrades() throws IOException {
if (loginSession != null) {
SubjectsSync subjectsSync = new SubjectsSync();
try {
subjectsSync.sync(loginSession);
syncGrades();
} catch (Exception e) {
Log.e(VulcanJobHelper.DEBUG_TAG, "Synchronisation of subjects failed", e);
throw new IOException(e.getCause());
}
} else {
Log.e(VulcanJobHelper.DEBUG_TAG, "Before sync, should login user to log",
new UnsupportedOperationException());
}
}
public void syncTimetable() throws IOException {
syncTimetable(null);
}
public void syncTimetable(@Nullable String date) throws IOException {
if (loginSession != null) {
TimetableSync timetableSync = new TimetableSync();
try {
timetableSync.sync(loginSession, date);
} catch (Exception e) {
Log.e(VulcanJobHelper.DEBUG_TAG, "Synchronization of timetable failed", e);
throw new IOException(e.getCause());
}
} else {
Log.e(VulcanJobHelper.DEBUG_TAG, "Before sync, should login user to log",
new UnsupportedOperationException());
}
}
}

View File

@ -0,0 +1,67 @@
package io.github.wulkanowy.ui.base;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import butterknife.Unbinder;
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;
private Unbinder unbinder;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this))
.applicationComponent(((WulkanowyApp) getApplication()).getApplicationComponent())
.build();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (unbinder != null) {
unbinder.unbind();
}
}
@Override
public void onError(int resId) {
onError(getString(resId));
}
@Override
public void onError(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
@Override
public void onNoNetworkError() {
onError(R.string.noInternet_text);
}
@Override
public boolean isNetworkConnected() {
return NetworkUtils.isOnline(getApplicationContext());
}
public ActivityComponent getActivityComponent() {
return activityComponent;
}
public void setButterKnife(Unbinder unbinder) {
this.unbinder = unbinder;
}
}

View File

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

View File

@ -0,0 +1,104 @@
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.view.View;
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;
public abstract class BaseFragment extends Fragment implements BaseContract.View {
private BaseActivity activity;
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();
}
@Override
public void onDestroyView() {
if (unbinder != null) {
unbinder.unbind();
}
super.onDestroyView();
}
@Override
public void onError(int resId) {
onError(getString(resId));
}
@Override
public void onError(String message) {
if (activity != null) {
activity.onError(message);
}
}
@Override
public void onNoNetworkError() {
onError(R.string.noInternet_text);
}
@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
}
}

View File

@ -0,0 +1,35 @@
package io.github.wulkanowy.ui.base;
import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract;
public class BasePresenter<V extends BaseContract.View> implements BaseContract.Presenter<V> {
private final RepositoryContract repository;
private V view;
@Inject
public BasePresenter(RepositoryContract repository) {
this.repository = repository;
}
@Override
public void onStart(V view) {
this.view = view;
}
@Override
public void onDestroy() {
view = null;
}
public final RepositoryContract getRepository() {
return repository;
}
public V getView() {
return view;
}
}

View File

@ -1,226 +1,223 @@
package io.github.wulkanowy.ui.login; package io.github.wulkanowy.ui.login;
import android.app.Activity; import android.animation.Animator;
import android.net.Uri; import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.design.widget.Snackbar;
import android.support.customtabs.CustomTabsIntent; import android.support.design.widget.TextInputLayout;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import java.util.LinkedHashMap; import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnEditorAction;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.services.Updater; import io.github.wulkanowy.ui.base.BaseActivity;
import io.github.wulkanowy.ui.main.MainActivity;
import io.github.wulkanowy.utils.AppConstant;
import io.github.wulkanowy.utils.CommonUtils;
import io.github.wulkanowy.utils.KeyboardUtils; import io.github.wulkanowy.utils.KeyboardUtils;
/** public class LoginActivity extends BaseActivity implements LoginContract.View {
* A login screen that offers login via email/password.
*/
public class LoginActivity extends Activity {
private float touchPosition; @BindView(R.id.login_activity_email_edit)
EditText emailView;
private EditText emailView; @BindView(R.id.login_activity_pass_edit)
EditText passwordView;
private EditText passwordView; @BindView(R.id.login_activity_symbol_edit)
AutoCompleteTextView symbolView;
private AutoCompleteTextView symbolView; @BindView(R.id.login_activity_form_scroll)
View loginFormView;
private Updater updater; @BindView(R.id.login_activity_progress_container)
View loadingBarView;
@BindView(R.id.login_activity_progress_text)
TextView loginProgressText;
@BindView(R.id.login_activity_symbol_text_input)
TextInputLayout symbolLayout;
@Inject
LoginContract.Presenter presenter;
private EditText requestedView;
public static Intent getStartIntent(Context context) {
return new Intent(context, LoginActivity.class);
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login); setContentView(R.layout.activity_login);
updater = new Updater(this).checkForUpdates(); setButterKnife(ButterKnife.bind(this));
getActivityComponent().inject(this);
// Set up the login form. presenter.onStart(this);
emailView = findViewById(R.id.email);
passwordView = findViewById(R.id.password);
symbolView = findViewById(R.id.symbol);
passwordView.setOnEditorActionListener(getTextViewSignInListener()); setUpOnCreate();
symbolView.setOnEditorActionListener(getTextViewSignInListener());
populateAutoComplete();
Button signInButton = findViewById(R.id.action_sign_in);
signInButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
attemptLogin();
}
});
findViewById(R.id.action_create_account).setOnClickListener(getButtonLinkListener(
"https://cufs.vulcan.net.pl/Default/AccountManage/CreateAccount"
));
findViewById(R.id.action_forgot_password).setOnClickListener(getButtonLinkListener(
"https://cufs.vulcan.net.pl/Default/AccountManage/UnlockAccount"
));
} }
@Override protected void setUpOnCreate() {
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { symbolView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
super.onRequestPermissionsResult(requestCode, permissions, grantResults); getResources().getStringArray(R.array.symbols)));
updater.onRequestPermissionsResult(requestCode, grantResults);
} }
private TextView.OnEditorActionListener getTextViewSignInListener() { @OnClick(R.id.login_activity_sign_button)
return new TextView.OnEditorActionListener() { void onLoginButtonClick() {
@Override presenter.attemptLogin(
public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { emailView.getText().toString(),
passwordView.getText().toString(),
symbolView.getText().toString());
}
@OnEditorAction(value = {R.id.login_activity_symbol_edit, R.id.login_activity_pass_edit})
boolean onEditorAction(int id) {
if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) { if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) {
attemptLogin(); onLoginButtonClick();
return true; return true;
} }
return false; return false;
} }
};
@OnClick(R.id.login_activity_create_text)
void onCreateAccountButtonClick() {
CommonUtils.openInternalBrowserViewer(getApplicationContext(),
AppConstant.VULCAN_CREATE_ACCOUNT_URL);
}
@OnClick(R.id.login_activity_forgot_text)
void onForgotPasswordButtonClick() {
CommonUtils.openInternalBrowserViewer(getApplicationContext(),
AppConstant.VULCAN_FORGOT_PASS_URL);
} }
private OnClickListener getButtonLinkListener(final String url) {
return new OnClickListener() {
@Override @Override
public void onClick(View view) { public void setErrorEmailRequired() {
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); emailView.requestFocus();
CustomTabsIntent customTabsIntent = builder.build(); emailView.setError(getString(R.string.error_field_required));
builder.setToolbarColor(getResources().getColor(R.color.colorPrimary)); requestedView = emailView;
customTabsIntent.launchUrl(view.getContext(), Uri.parse(url));
}
};
} }
private void populateAutoComplete() { @Override
// Get the string array public void setErrorEmailInvalid() {
String[] countries = getResources().getStringArray(R.array.symbols); emailView.requestFocus();
// Create the adapter and set it to the AutoCompleteTextView emailView.setError(getString(R.string.error_invalid_email));
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, requestedView = emailView;
countries);
symbolView.setAdapter(adapter);
} }
/** @Override
* Attempts to sign in the account specified by the login form. public void setErrorPassRequired() {
*/ passwordView.requestFocus();
private void attemptLogin() { passwordView.setError(getString(R.string.error_field_required));
// Reset errors. requestedView = passwordView;
}
@Override
public void setErrorPassInvalid() {
passwordView.requestFocus();
passwordView.setError(getString(R.string.error_invalid_password));
requestedView = passwordView;
}
@Override
public void setErrorPassIncorrect() {
passwordView.requestFocus();
passwordView.setError(getString(R.string.error_incorrect_password));
requestedView = passwordView;
}
@Override
public void setErrorSymbolRequired() {
symbolLayout.setVisibility(View.VISIBLE);
symbolView.setError(getString(R.string.error_bad_account_permission));
symbolView.requestFocus();
requestedView = symbolView;
}
@Override
public void resetViewErrors() {
emailView.setError(null); emailView.setError(null);
passwordView.setError(null); passwordView.setError(null);
// Store values at the time of the login attempt.
String email = emailView.getText().toString();
String password = passwordView.getText().toString();
String symbol = symbolView.getText().toString();
boolean cancel = false;
View focusView = null;
// Check for a valid password.
if (TextUtils.isEmpty(password)) {
passwordView.setError(getString(R.string.error_field_required));
focusView = passwordView;
cancel = true;
} else if (!isPasswordValid(password)) {
passwordView.setError(getString(R.string.error_invalid_password));
focusView = passwordView;
cancel = true;
}
// Check for a valid email address.
if (TextUtils.isEmpty(email)) {
emailView.setError(getString(R.string.error_field_required));
focusView = emailView;
cancel = true;
} else if (!isEmailValid(email)) {
emailView.setError(getString(R.string.error_invalid_email));
focusView = emailView;
cancel = true;
}
// Check for a valid symbol.
if (TextUtils.isEmpty(symbol)) {
symbol = "Default";
}
String[] keys = getResources().getStringArray(R.array.symbols);
String[] values = getResources().getStringArray(R.array.symbols_values);
LinkedHashMap<String, String> map = new LinkedHashMap<>();
for (int i = 0; i < Math.min(keys.length, values.length); ++i) {
map.put(keys[i], values[i]);
}
if (map.containsKey(symbol)) {
symbol = map.get(symbol);
}
if (cancel) {
// There was an error; don't attempt login and focus the first
// form field with an error.
focusView.requestFocus();
} else {
// Show a progress spinner and kick off a background task to
// perform the user login attempt.
LoginTask authTask = new LoginTask(this, email, password, symbol);
authTask.showProgress(true);
authTask.execute();
KeyboardUtils.hideSoftInput(this);
}
}
private boolean isEmailValid(String email) {
return email.contains("@") || email.contains("\\\\");
}
private boolean isPasswordValid(String password) {
return password.length() > 4;
} }
@Override @Override
public boolean dispatchTouchEvent(MotionEvent ev) { public void showSoftInput() {
KeyboardUtils.showSoftInput(requestedView, this);
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
touchPosition = ev.getY();
} }
if (ev.getAction() == MotionEvent.ACTION_UP) {
float releasePosition = ev.getY();
if (touchPosition - releasePosition == 0) { @Override
View view = getCurrentFocus(); public void hideSoftInput() {
if (view != null && (ev.getAction() == MotionEvent.ACTION_UP
|| ev.getAction() == MotionEvent.ACTION_MOVE) && view instanceof EditText
&& !view.getClass().getName().startsWith("android.webkit.")) {
int[] coordinators = new int[2];
view.getLocationOnScreen(coordinators);
float x = ev.getRawX() + view.getLeft() - coordinators[0];
float y = ev.getRawY() + view.getTop() - coordinators[1];
if (x < view.getLeft() || x > view.getRight() || y < view.getTop()
|| y > view.getBottom()) {
KeyboardUtils.hideSoftInput(this); 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));
} }
@Override
public void setStepTwoLoginProgress() {
onLoginProgressUpdate("2", getString(R.string.step_synchronization));
} }
return super.dispatchTouchEvent(ev);
@Override
public void openMainActivity() {
startActivity(MainActivity.getStartIntent(this));
finish();
}
@Override
public void showLoginProgress(final boolean show) {
int animTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
loginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
loginFormView.animate().setDuration(animTime).alpha(
show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
loginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
}
});
loadingBarView.setVisibility(show ? View.VISIBLE : View.GONE);
loadingBarView.animate().setDuration(animTime).alpha(
show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
loadingBarView.setVisibility(show ? View.VISIBLE : View.GONE);
}
});
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
updater.onDestroy(this); presenter.onDestroy();
}
private void onLoginProgressUpdate(String step, String message) {
loginProgressText.setText(String.format("%1$s/2 - %2$s...", step, message));
} }
} }

View File

@ -0,0 +1,53 @@
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 {
interface View extends BaseContract.View {
void setErrorEmailRequired();
void setErrorPassRequired();
void setErrorSymbolRequired();
void setErrorEmailInvalid();
void setErrorPassInvalid();
void setErrorPassIncorrect();
void resetViewErrors();
void setStepOneLoginProgress();
void setStepTwoLoginProgress();
void openMainActivity();
void showLoginProgress(boolean show);
void showSoftInput();
void hideSoftInput();
}
@PerActivity
interface Presenter extends BaseContract.Presenter<View> {
void attemptLogin(String email, String password, String symbol);
void onStartAsync();
void onLoginProgress(int step);
void onEndAsync(boolean success, Exception exception);
void onCanceledAsync();
RepositoryContract getRepository();
}
}

View File

@ -0,0 +1,143 @@
package io.github.wulkanowy.ui.login;
import android.text.TextUtils;
import java.util.LinkedHashMap;
import javax.inject.Inject;
import io.github.wulkanowy.api.login.AccountPermissionException;
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;
public class LoginPresenter extends BasePresenter<LoginContract.View>
implements LoginContract.Presenter {
private LoginTask loginAsync;
@Inject
LoginPresenter(RepositoryContract repository) {
super(repository);
}
@Override
public void onDestroy() {
super.onDestroy();
if (loginAsync != null) {
loginAsync.cancel(true);
loginAsync = null;
}
}
@Override
public void attemptLogin(String email, String password, String symbol) {
getView().resetViewErrors();
if (!isAllFieldCorrect(password, email)) {
getView().showSoftInput();
return;
}
if (getView().isNetworkConnected()) {
// Dopóki używamy AsyncTask presenter będzie musiał "wiedzieć" o AsyncTaskach
loginAsync = new LoginTask(this,
email,
password,
getNormalizedSymbol(symbol));
loginAsync.execute();
} else {
getView().onNoNetworkError();
}
getView().hideSoftInput();
}
@Override
public void onStartAsync() {
getView().showLoginProgress(true);
}
@Override
public void onLoginProgress(int step) {
if (step == 1) {
getView().setStepOneLoginProgress();
} else if (step == 2) {
getView().setStepTwoLoginProgress();
}
}
@Override
public void onEndAsync(boolean success, Exception exception) {
if (success) {
getView().openMainActivity();
} else if (exception instanceof BadCredentialsException) {
getView().setErrorPassIncorrect();
getView().showSoftInput();
getView().showLoginProgress(false);
} else if (exception instanceof AccountPermissionException) {
getView().setErrorSymbolRequired();
getView().showSoftInput();
getView().showLoginProgress(false);
} else {
getView().onError(getRepository().getErrorLoginMessage(exception));
getView().showLoginProgress(false);
}
}
@Override
public void onCanceledAsync() {
getView().showLoginProgress(false);
}
private boolean isEmailValid(String email) {
return email.contains("@") || email.contains("\\\\");
}
private boolean isPasswordValid(String password) {
return password.length() > 4;
}
private String getNormalizedSymbol(String symbol) {
if (TextUtils.isEmpty(symbol)) {
return AppConstant.DEFAULT_SYMBOL;
}
String[] keys = getRepository().getSymbolsKeysArray();
String[] values = getRepository().getSymbolsValuesArray();
LinkedHashMap<String, String> map = new LinkedHashMap<>();
for (int i = 0; i < Math.min(keys.length, values.length); ++i) {
map.put(keys[i], values[i]);
}
if (map.containsKey(symbol)) {
return map.get(symbol);
}
return AppConstant.DEFAULT_SYMBOL;
}
private boolean isAllFieldCorrect(String password, String email) {
boolean correct = true;
if (TextUtils.isEmpty(password)) {
getView().setErrorPassRequired();
correct = false;
} else if (!isPasswordValid(password)) {
getView().setErrorPassInvalid();
correct = false;
}
if (TextUtils.isEmpty(email)) {
getView().setErrorEmailRequired();
correct = false;
} else if (!isEmailValid(email)) {
getView().setErrorEmailInvalid();
correct = false;
}
return correct;
}
}

View File

@ -1,64 +1,21 @@
package io.github.wulkanowy.ui.login; package io.github.wulkanowy.ui.login;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AlertDialog;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import com.crashlytics.android.Crashlytics; public class LoginTask extends AsyncTask<Void, Integer, Boolean> {
import com.crashlytics.android.answers.Answers;
import com.crashlytics.android.answers.CustomEvent;
import java.io.IOException; private String email;
import java.lang.ref.WeakReference;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import io.github.wulkanowy.R; private String password;
import io.github.wulkanowy.WulkanowyApp;
import io.github.wulkanowy.api.login.AccountPermissionException;
import io.github.wulkanowy.api.login.BadCredentialsException;
import io.github.wulkanowy.api.login.NotLoggedInErrorException;
import io.github.wulkanowy.api.login.VulcanOfflineException;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import io.github.wulkanowy.services.jobs.FullSyncJob;
import io.github.wulkanowy.services.sync.LoginSession;
import io.github.wulkanowy.services.sync.VulcanSync;
import io.github.wulkanowy.ui.main.DashboardActivity;
import io.github.wulkanowy.utils.KeyboardUtils;
import io.github.wulkanowy.utils.NetworkUtils;
import io.github.wulkanowy.utils.security.CryptoException;
/** private String symbol;
* Represents an asynchronous login/registration task used to authenticate
* the user.
*/
public class LoginTask extends AsyncTask<Void, String, Integer> {
private final String email; private LoginContract.Presenter presenter;
private final String password; private Exception exception;
private final String symbol; LoginTask(LoginContract.Presenter presenter, String email, String password, String symbol) {
this.presenter = presenter;
private WeakReference<Activity> activity;
private WeakReference<View> progressView;
private WeakReference<View> loginFormView;
private WeakReference<TextView> showText;
LoginTask(Activity activity, String email, String password, String symbol) {
this.activity = new WeakReference<>(activity);
this.email = email; this.email = email;
this.password = password; this.password = password;
this.symbol = symbol; this.symbol = symbol;
@ -66,159 +23,36 @@ public class LoginTask extends AsyncTask<Void, String, Integer> {
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
showText = new WeakReference<>((TextView) activity.get().findViewById(R.id.login_progress_text)); presenter.onStartAsync();
} }
@Override @Override
protected Integer doInBackground(Void... params) { protected Boolean doInBackground(Void... params) {
if (NetworkUtils.isOnline(activity.get())) {
DaoSession daoSession = ((WulkanowyApp) activity.get().getApplication()).getDaoSession();
VulcanSync vulcanSync = new VulcanSync(new LoginSession());
try { try {
publishProgress("1", activity.get().getResources().getString(R.string.step_login)); publishProgress(1);
vulcanSync.firstLoginSignInStep(activity.get(), daoSession, email, password, symbol); presenter.getRepository().loginUser(email, password, symbol);
publishProgress("2", activity.get().getResources().getString(R.string.step_synchronization)); publishProgress(2);
vulcanSync.syncAll(); presenter.getRepository().syncAll();
} catch (BadCredentialsException e) { } catch (Exception e) {
return R.string.login_bad_credentials_text; exception = e;
} catch (AccountPermissionException e) { return false;
return R.string.error_bad_account_permission;
} catch (CryptoException e) {
return R.string.encrypt_failed_text;
} catch (UnknownHostException e) {
return R.string.noInternet_text;
} catch (SocketTimeoutException e) {
return R.string.generic_timeout_error;
} catch (NotLoggedInErrorException | IOException e) {
return R.string.login_denied_text;
} catch (VulcanOfflineException e) {
return R.string.error_host_offline;
} catch (UnsupportedOperationException e) {
return -1;
} catch (Throwable e) {
Crashlytics.logException(e);
return R.string.login_denied_text;
}
new FullSyncJob().scheduledJob(activity.get());
return R.string.login_accepted_text;
} else {
return R.string.noInternet_text;
} }
return true;
} }
@Override @Override
protected void onProgressUpdate(String... progress) { protected void onProgressUpdate(Integer... progress) {
showText.get().setText(String.format("%1$s/2 - %2$s...", progress[0], progress[1])); presenter.onLoginProgress(progress[0]);
} }
@Override @Override
protected void onPostExecute(final Integer messageID) { protected void onPostExecute(Boolean success) {
showProgress(false); presenter.onEndAsync(success, exception);
switch (messageID) {
// if success
case R.string.login_accepted_text:
logFirstLoginAction(true, activity.get().getString(messageID));
Intent intent = new Intent(activity.get(), DashboardActivity.class);
activity.get().finish();
activity.get().startActivity(intent);
break;
// if bad credentials entered
case R.string.login_bad_credentials_text:
logFirstLoginAction(false, activity.get().getString(messageID));
EditText passwordView = activity.get().findViewById(R.id.password);
passwordView.setError(activity.get().getString(R.string.error_incorrect_password));
passwordView.requestFocus();
KeyboardUtils.showSoftInput(passwordView, activity.get());
break;
// if no permission
case R.string.error_bad_account_permission:
logFirstLoginAction(false, activity.get().getString(messageID));
// Change to visible symbol input view
TextInputLayout symbolLayout = activity.get().findViewById(R.id.to_symbol_input_layout);
symbolLayout.setVisibility(View.VISIBLE);
EditText symbolView = activity.get().findViewById(R.id.symbol);
symbolView.setError(activity.get().getString(R.string.error_bad_account_permission));
symbolView.requestFocus();
KeyboardUtils.showSoftInput(symbolView, activity.get());
break;
// if rooted and SDK < 18
case -1:
logFirstLoginAction(false, "Device rooted");
final AlertDialog.Builder alertDialog = new AlertDialog.Builder(activity.get())
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.alert_dialog_blocked_app)
.setMessage(R.string.alert_dialog_blocked_app_message)
.setPositiveButton(R.string.generic_dialog_close, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
});
alertDialog.show();
break;
default:
logFirstLoginAction(false, activity.get().getString(messageID));
Snackbar.make(activity.get().findViewById(R.id.fragment_container),
messageID, Snackbar.LENGTH_LONG).show();
break;
}
}
private void logFirstLoginAction(boolean success, String message) {
Answers.getInstance().logCustom(new CustomEvent("First login")
.putCustomAttribute("Symbol", symbol)
.putCustomAttribute("Success", success ? 1 : 0)
.putCustomAttribute("Message", message));
} }
@Override @Override
protected void onCancelled() { protected void onCancelled() {
showProgress(false); presenter.onCanceledAsync();
}
/**
* Shows the progress UI and hides the login form.
*/
void showProgress(final boolean show) {
loginFormView = new WeakReference<>(activity.get().findViewById(R.id.login_form));
progressView = new WeakReference<>(activity.get().findViewById(R.id.login_progress));
int animTime = activity.get().getResources().getInteger(android.R.integer.config_shortAnimTime);
changeLoginFormVisibility(show, animTime);
changeProgressVisibility(show, animTime);
}
private void changeLoginFormVisibility(final boolean show, final int animTime) {
loginFormView.get().setVisibility(show ? View.GONE : View.VISIBLE);
loginFormView.get().animate().setDuration(animTime).alpha(
show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
loginFormView.get().setVisibility(show ? View.GONE : View.VISIBLE);
}
});
}
private void changeProgressVisibility(final boolean show, final int animTime) {
progressView.get().setVisibility(show ? View.VISIBLE : View.GONE);
progressView.get().animate().setDuration(animTime).alpha(
show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
progressView.get().setVisibility(show ? View.VISIBLE : View.GONE);
}
});
} }
} }

View File

@ -1,189 +0,0 @@
package io.github.wulkanowy.ui.main;
import android.app.Activity;
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.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager;
import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem;
import io.github.wulkanowy.R;
import io.github.wulkanowy.WulkanowyApp;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import io.github.wulkanowy.utils.NetworkUtils;
public abstract class AbstractFragment<T extends AbstractExpandableHeaderItem> extends Fragment
implements AsyncResponse<T> {
private FlexibleAdapter<T> flexibleAdapter;
private List<T> itemList = new ArrayList<>();
private WeakReference<Activity> activityWeakReference;
private SwipeRefreshLayout swipeRefreshLayout;
private RecyclerView recyclerViewLayout;
private DaoSession daoSession;
private long userId;
public AbstractFragment() {
//empty constructor for fragments
}
public long getUserId() {
return userId;
}
public SwipeRefreshLayout getRefreshLayoutView() {
return swipeRefreshLayout;
}
public DaoSession getDaoSession() {
return daoSession;
}
public Activity getActivityWeakReference() {
return activityWeakReference.get();
}
public abstract int getLayoutId();
public abstract int getRecyclerViewId();
public abstract int getLoadingBarId();
public abstract int getRefreshLayoutId();
public abstract List<T> getItems() throws Exception;
public abstract void onRefresh() throws Exception;
public abstract void onPostRefresh(int stringResult);
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(getLayoutId(), container, false);
recyclerViewLayout = view.findViewById(getRecyclerViewId());
swipeRefreshLayout = view.findViewById(getRefreshLayoutId());
setUpRefreshLayout(swipeRefreshLayout);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getActivity() != null && getView() != null) {
activityWeakReference = new WeakReference<Activity>(getActivity());
daoSession = ((WulkanowyApp) getActivity().getApplication()).getDaoSession();
userId = getActivity().getSharedPreferences("LoginData", Context.MODE_PRIVATE)
.getLong("userId", 0);
if (itemList != null)
if (itemList.isEmpty()) {
flexibleAdapter = getFlexibleAdapter(itemList);
setAdapterOnRecyclerView(recyclerViewLayout);
if (getUserVisibleHint()) {
new DatabaseQueryTask(this).execute();
}
} else {
setAdapterOnRecyclerView(recyclerViewLayout);
setLoadingBarInvisible(getView());
}
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isResumed() && isVisibleToUser && flexibleAdapter.getItemCount() == 0) {
new DatabaseQueryTask(this).execute();
}
}
@Override
public void onQuarryProcessFinish(@NonNull List<T> resultItemList) {
itemList = resultItemList;
flexibleAdapter = getFlexibleAdapter(itemList);
setAdapterOnRecyclerView(recyclerViewLayout);
if (getView() != null) {
setLoadingBarInvisible(getView());
}
}
@Override
public void onRefreshProcessFinish(@Nullable List<T> resultItemList, int stringEventId) {
if (resultItemList != null) {
itemList = resultItemList;
updateDataInRecyclerView();
}
onPostRefresh(stringEventId);
getRefreshLayoutView().setRefreshing(false);
}
@NonNull
protected FlexibleAdapter<T> getFlexibleAdapter(@NonNull List<T> itemList) {
return new FlexibleAdapter<>(itemList)
.setAutoCollapseOnExpand(true)
.setAutoScrollOnExpand(true)
.expandItemsAtStartUp();
}
@NonNull
private SwipeRefreshLayout.OnRefreshListener getDefaultRefreshListener() {
return new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
if (NetworkUtils.isOnline(getContext())) {
new RefreshTask(AbstractFragment.this).execute();
} else {
Toast.makeText(getContext(), R.string.noInternet_text, Toast.LENGTH_SHORT).show();
swipeRefreshLayout.setRefreshing(false);
}
}
};
}
private void updateDataInRecyclerView() {
flexibleAdapter.updateDataSet(itemList);
setAdapterOnRecyclerView(recyclerViewLayout);
}
protected void setUpRefreshLayout(@NonNull SwipeRefreshLayout swipeRefreshLayout) {
swipeRefreshLayout.setColorSchemeResources(android.R.color.black);
swipeRefreshLayout.setOnRefreshListener(getDefaultRefreshListener());
}
protected final void setLoadingBarInvisible(@NonNull View mainView) {
mainView.findViewById(getLoadingBarId()).setVisibility(View.INVISIBLE);
}
protected void setAdapterOnRecyclerView(@NonNull RecyclerView recyclerView) {
recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(getActivityWeakReference()));
recyclerView.setAdapter(flexibleAdapter);
}
}

View File

@ -1,14 +0,0 @@
package io.github.wulkanowy.ui.main;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.List;
import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem;
public interface AsyncResponse<T extends AbstractExpandableHeaderItem> {
void onQuarryProcessFinish(@NonNull List<T> resultItemList);
void onRefreshProcessFinish(@Nullable List<T> resultItemList, int stringErrorId);
}

View File

@ -1,133 +0,0 @@
package io.github.wulkanowy.ui.main;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import io.github.wulkanowy.R;
import io.github.wulkanowy.services.Updater;
import io.github.wulkanowy.ui.main.attendance.AttendanceFragment;
import io.github.wulkanowy.ui.main.board.BoardFragment;
import io.github.wulkanowy.ui.main.grades.GradesFragment;
import io.github.wulkanowy.ui.main.timetable.TimetableFragment;
public class DashboardActivity extends AppCompatActivity {
private Fragment currentFragment;
private GradesFragment gradesFragment = new GradesFragment();
private AttendanceFragment attendanceFragment = new AttendanceFragment();
private BoardFragment boardFragment = new BoardFragment();
private TimetableFragment timetableFragment = new TimetableFragment();
private Updater updater;
private boolean showed;
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_marks:
setTitle(R.string.grades_text);
currentFragment = gradesFragment;
break;
case R.id.navigation_attendance:
setTitle(R.string.attendance_text);
currentFragment = attendanceFragment;
break;
case R.id.navigation_lessonplan:
setTitle(R.string.lessonplan_text);
currentFragment = timetableFragment;
break;
case R.id.navigation_dashboard:
default:
setTitle(R.string.dashboard_text);
currentFragment = boardFragment;
break;
}
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, currentFragment);
transaction.commit();
return true;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dashboard);
BottomNavigationView navigation = findViewById(R.id.navigation);
navigation.setSelectedItemId(R.id.navigation_marks);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
if (savedInstanceState != null) {
currentFragment = getSupportFragmentManager().getFragment(savedInstanceState, "currentFragment");
setTitle(savedInstanceState.getString("activityTitle"));
} else {
currentFragment = gradesFragment;
setTitle(R.string.grades_text);
}
int cardID = getIntent().getIntExtra("cardID", 0);
if (cardID == 1) {
currentFragment = gradesFragment;
}
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, currentFragment).commit();
}
@Override
protected void onStart() {
super.onStart();
if (!showed) {
updater = new Updater(this).checkForUpdates();
showed = true;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
updater.onRequestPermissionsResult(requestCode, grantResults);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("activityTitle", getTitle().toString());
getSupportFragmentManager().putFragment(outState, "currentFragment", currentFragment);
}
public void onBackPressed() {
BottomNavigationView navigation = findViewById(R.id.navigation);
if (navigation.getSelectedItemId() != R.id.navigation_dashboard) {
navigation.setSelectedItemId(R.id.navigation_dashboard);
} else if (navigation.getSelectedItemId() == R.id.navigation_dashboard) {
moveTaskToBack(true);
}
}
@Override
public void onDestroy() {
super.onDestroy();
updater.onDestroy(this);
}
}

View File

@ -1,30 +0,0 @@
package io.github.wulkanowy.ui.main;
import android.os.AsyncTask;
import java.util.List;
public class DatabaseQueryTask extends AsyncTask<Void, Void, List<?>> {
private AbstractFragment abstractFragment;
public DatabaseQueryTask(AbstractFragment<?> abstractFragment) {
this.abstractFragment = abstractFragment;
}
@Override
protected List<?> doInBackground(Void... voids) {
try {
return abstractFragment.getItems();
} catch (Exception e) {
return null;
}
}
@SuppressWarnings("unchecked")
@Override
protected void onPostExecute(List<?> objects) {
super.onPostExecute(objects);
abstractFragment.onQuarryProcessFinish(objects);
}
}

View File

@ -0,0 +1,153 @@
package io.github.wulkanowy.ui.main;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.view.View;
import com.aurelhubert.ahbottomnavigation.AHBottomNavigation;
import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem;
import com.aurelhubert.ahbottomnavigation.AHBottomNavigationViewPager;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.github.wulkanowy.R;
import io.github.wulkanowy.services.SyncJob;
import io.github.wulkanowy.ui.base.BaseActivity;
import io.github.wulkanowy.ui.main.dashboard.DashboardFragment;
import io.github.wulkanowy.ui.main.grades.GradesFragment;
import io.github.wulkanowy.ui.main.timetable.TimetableFragment;
public class MainActivity extends BaseActivity implements MainContract.View,
AHBottomNavigation.OnTabSelectedListener, OnFragmentIsReadyListener {
private int initTabPosition = 0;
@BindView(R.id.main_activity_nav)
AHBottomNavigation bottomNavigation;
@BindView(R.id.main_activity_view_pager)
AHBottomNavigationViewPager viewPager;
@BindView(R.id.main_activity_progress_bar)
View progressBar;
@Inject
MainPagerAdapter pagerAdapter;
@Inject
MainContract.Presenter presenter;
public static Intent getStartIntent(Context context) {
return new Intent(context, MainActivity.class);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initTabPosition = getIntent().getIntExtra(SyncJob.EXTRA_INTENT_KEY, initTabPosition);
getActivityComponent().inject(this);
setButterKnife(ButterKnife.bind(this));
presenter.onStart(this);
initiationViewPager();
initiationBottomNav();
}
@Override
public void showProgressBar(boolean show) {
progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
viewPager.setVisibility(show ? View.INVISIBLE : View.VISIBLE);
bottomNavigation.setVisibility(show ? View.INVISIBLE : View.VISIBLE);
}
@Override
public void showActionBar() {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.show();
}
}
@Override
public void hideActionBar() {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
}
@Override
public boolean onTabSelected(int position, boolean wasSelected) {
presenter.onTabSelected(position, wasSelected);
return true;
}
@Override
public void setCurrentPage(int position) {
viewPager.setCurrentItem(position, false);
}
@Override
public void onFragmentIsReady() {
presenter.onFragmentIsReady();
}
private void initiationBottomNav() {
bottomNavigation.addItem(new AHBottomNavigationItem(
getString(R.string.grades_text),
getResources().getDrawable(R.drawable.icon_grade_26dp)
));
bottomNavigation.addItem(new AHBottomNavigationItem(
getString(R.string.attendance_text),
getResources().getDrawable(R.drawable.icon_attendance_24dp)
));
bottomNavigation.addItem(new AHBottomNavigationItem(
getString(R.string.dashboard_text),
getResources().getDrawable(R.drawable.ic_dashboard_black_24dp)
));
bottomNavigation.addItem(new AHBottomNavigationItem(
getString(R.string.lessonplan_text),
getResources().getDrawable(R.drawable.icon_lessonplan_24dp)
));
bottomNavigation.addItem(new AHBottomNavigationItem(
getString(R.string.settings_text),
getResources().getDrawable(R.drawable.icon_other_24dp)
));
bottomNavigation.setAccentColor(getResources().getColor(R.color.colorPrimary));
bottomNavigation.setInactiveColor(Color.BLACK);
bottomNavigation.setBackgroundColor(getResources().getColor(R.color.colorBackgroundBottomNavi));
bottomNavigation.setTitleState(AHBottomNavigation.TitleState.ALWAYS_SHOW);
bottomNavigation.setOnTabSelectedListener(this);
bottomNavigation.setCurrentItem(initTabPosition);
bottomNavigation.setBehaviorTranslationEnabled(false);
}
private void initiationViewPager() {
pagerAdapter.addFragment(new GradesFragment());
pagerAdapter.addFragment(new DashboardFragment());
pagerAdapter.addFragment(new DashboardFragment());
pagerAdapter.addFragment(new TimetableFragment());
pagerAdapter.addFragment(new DashboardFragment());
viewPager.setPagingEnabled(false);
viewPager.setAdapter(pagerAdapter);
viewPager.setOffscreenPageLimit(4);
viewPager.setCurrentItem(initTabPosition, false);
}
@Override
protected void onDestroy() {
super.onDestroy();
presenter.onDestroy();
}
}

View File

@ -0,0 +1,26 @@
package io.github.wulkanowy.ui.main;
import io.github.wulkanowy.di.annotations.PerActivity;
import io.github.wulkanowy.ui.base.BaseContract;
public interface MainContract {
interface View extends BaseContract.View {
void setCurrentPage(int position);
void showProgressBar(boolean show);
void showActionBar();
void hideActionBar();
}
@PerActivity
interface Presenter extends BaseContract.Presenter<View> {
void onTabSelected(int position, boolean wasSelected);
void onFragmentIsReady();
}
}

View File

@ -0,0 +1,31 @@
package io.github.wulkanowy.ui.main;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import java.util.ArrayList;
import java.util.List;
public class MainPagerAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragmentList = new ArrayList<>();
public MainPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
void addFragment(Fragment fragment) {
fragmentList.add(fragment);
}
@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
@Override
public int getCount() {
return fragmentList.size();
}
}

View File

@ -0,0 +1,44 @@
package io.github.wulkanowy.ui.main;
import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.ui.base.BasePresenter;
public class MainPresenter extends BasePresenter<MainContract.View>
implements MainContract.Presenter {
private int fragmentCount = 0;
@Inject
MainPresenter(RepositoryContract repository) {
super(repository);
}
@Override
public void onStart(MainContract.View view) {
super.onStart(view);
getView().showProgressBar(true);
getView().hideActionBar();
}
@Override
public void onTabSelected(int position, boolean wasSelected) {
if (!wasSelected) {
getView().setCurrentPage(position);
}
}
@Override
public void onFragmentIsReady() {
if (fragmentCount < 5) {
fragmentCount++;
}
if (fragmentCount == 5) {
getView().showActionBar();
getView().showProgressBar(false);
}
}
}

View File

@ -0,0 +1,6 @@
package io.github.wulkanowy.ui.main;
public interface OnFragmentIsReadyListener {
void onFragmentIsReady();
}

View File

@ -1,56 +0,0 @@
package io.github.wulkanowy.ui.main;
import android.os.AsyncTask;
import android.util.Log;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.List;
import io.github.wulkanowy.R;
import io.github.wulkanowy.api.login.VulcanOfflineException;
public class RefreshTask extends AsyncTask<Void, Void, List<?>> {
public static final String DEBUG_TAG = "RefreshTask";
private int stringEventId = 0;
private AbstractFragment abstractFragment;
public RefreshTask(AbstractFragment abstractFragment) {
this.abstractFragment = abstractFragment;
}
@Override
protected List<?> doInBackground(Void... voids) {
try {
abstractFragment.onRefresh();
return abstractFragment.getItems();
} catch (UnknownHostException e) {
stringEventId = R.string.noInternet_text;
Log.i(DEBUG_TAG, "Synchronization is failed because occur problem with internet",
e.getCause());
return null;
} catch (SocketTimeoutException e) {
stringEventId = R.string.generic_timeout_error;
Log.i(DEBUG_TAG, "Too long wait for connection with internet", e);
return null;
} catch (VulcanOfflineException e) {
stringEventId = R.string.error_host_offline;
Log.i(DEBUG_TAG, "VULCAN services is offline");
return null;
} catch (Exception e) {
stringEventId = R.string.refresh_error_text;
Log.e(DEBUG_TAG, "There was a sync problem", e);
return null;
}
}
@SuppressWarnings("unchecked")
@Override
protected void onPostExecute(List<?> objects) {
super.onPostExecute(objects);
abstractFragment.onRefreshProcessFinish(objects, stringEventId);
}
}

View File

@ -0,0 +1,23 @@
package io.github.wulkanowy.ui.main.attendance;
import io.github.wulkanowy.di.annotations.PerFragment;
import io.github.wulkanowy.ui.base.BaseContract;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
public interface AttendanceContract {
interface View extends BaseContract.View {
void setActivityTitle();
boolean isMenuVisible();
}
@PerFragment
interface Presenter extends BaseContract.Presenter<View> {
void onStart(View view, OnFragmentIsReadyListener listener);
void onFragmentVisible(boolean isVisible);
}
}

View File

@ -2,18 +2,58 @@ package io.github.wulkanowy.ui.main.attendance;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import io.github.wulkanowy.R; import javax.inject.Inject;
public class AttendanceFragment extends Fragment { import butterknife.ButterKnife;
import io.github.wulkanowy.R;
import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
public class AttendanceFragment extends BaseFragment implements AttendanceContract.View {
@Inject
AttendanceContract.Presenter presenter;
public AttendanceFragment() {
// empty constructor for fragment
}
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_attendance, container, false); View view = inflater.inflate(R.layout.fragment_attendance, container, false);
FragmentComponent component = getFragmentComponent();
if (component != null) {
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
presenter.onStart(this, (OnFragmentIsReadyListener) getActivity());
}
return view;
}
@Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
if (presenter != null) {
presenter.onFragmentVisible(menuVisible);
}
}
@Override
public void setActivityTitle() {
setTitle(getString(R.string.dashboard_text));
}
@Override
public void onDestroyView() {
super.onDestroyView();
presenter.onDestroy();
} }
} }

View File

@ -0,0 +1,37 @@
package io.github.wulkanowy.ui.main.attendance;
import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
implements AttendanceContract.Presenter {
private OnFragmentIsReadyListener listener;
@Inject
AttendancePresenter(RepositoryContract repository) {
super(repository);
}
@Override
public void onStart(AttendanceContract.View view, OnFragmentIsReadyListener listener) {
super.onStart(view);
this.listener = listener;
if (getView().isMenuVisible()) {
getView().setActivityTitle();
}
this.listener.onFragmentIsReady();
}
@Override
public void onFragmentVisible(boolean isVisible) {
if (isVisible) {
getView().setActivityTitle();
}
}
}

View File

@ -1,19 +0,0 @@
package io.github.wulkanowy.ui.main.board;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import io.github.wulkanowy.R;
public class BoardFragment extends Fragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_board, container, false);
}
}

View File

@ -0,0 +1,23 @@
package io.github.wulkanowy.ui.main.dashboard;
import io.github.wulkanowy.di.annotations.PerActivity;
import io.github.wulkanowy.ui.base.BaseContract;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
public interface DashboardContract {
interface View extends BaseContract.View {
void setActivityTitle();
boolean isMenuVisible();
}
@PerActivity
interface Presenter extends BaseContract.Presenter<View> {
void onStart(View view, OnFragmentIsReadyListener listener);
void onFragmentVisible(boolean isVisible);
}
}

View File

@ -0,0 +1,48 @@
package io.github.wulkanowy.ui.main.dashboard;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import javax.inject.Inject;
import butterknife.ButterKnife;
import io.github.wulkanowy.R;
import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
public class DashboardFragment extends BaseFragment implements DashboardContract.View {
@Inject
DashboardContract.Presenter presenter;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_board, container, false);
FragmentComponent component = getFragmentComponent();
if (component != null) {
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
presenter.onStart(this, (OnFragmentIsReadyListener) getActivity());
}
return view;
}
@Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
if (presenter != null) {
presenter.onFragmentVisible(menuVisible);
}
}
@Override
public void setActivityTitle() {
setTitle(getString(R.string.dashboard_text));
}
}

View File

@ -0,0 +1,37 @@
package io.github.wulkanowy.ui.main.dashboard;
import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
public class DashboardPresenter extends BasePresenter<DashboardContract.View>
implements DashboardContract.Presenter {
private OnFragmentIsReadyListener listener;
@Inject
DashboardPresenter(RepositoryContract repository) {
super(repository);
}
@Override
public void onStart(DashboardContract.View view, OnFragmentIsReadyListener listener) {
super.onStart(view);
this.listener = listener;
if (getView().isMenuVisible()) {
getView().setActivityTitle();
}
this.listener.onFragmentIsReady();
}
@Override
public void onFragmentVisible(boolean isVisible) {
if (isVisible) {
getView().setActivityTitle();
}
}
}

View File

@ -0,0 +1,117 @@
package io.github.wulkanowy.ui.main.grades;
import android.content.res.Resources;
import android.view.View;
import android.widget.TextView;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem;
import eu.davidea.viewholders.ExpandableViewHolder;
import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.Subject;
import io.github.wulkanowy.utils.AverageCalculator;
public class GradeHeaderItem
extends AbstractExpandableHeaderItem<GradeHeaderItem.HeaderViewHolder, GradesSubItem> {
private Subject subject;
GradeHeaderItem(Subject subject) {
this.subject = subject;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GradeHeaderItem that = (GradeHeaderItem) o;
return new EqualsBuilder()
.append(subject, that.subject)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(subject)
.toHashCode();
}
@Override
public int getLayoutRes() {
return R.layout.grade_header;
}
@Override
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
return new HeaderViewHolder(view, adapter);
}
@Override
public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List payloads) {
holder.onBind(subject, getSubItems());
}
static class HeaderViewHolder extends ExpandableViewHolder {
@BindView(R.id.grade_header_subject_text)
TextView subjectName;
@BindView(R.id.grade_header_average_text)
TextView averageText;
@BindView(R.id.grade_header_number_of_grade_text)
TextView numberText;
@BindView(R.id.grade_header_alert_image)
View alertImage;
Resources resources;
HeaderViewHolder(View view, FlexibleAdapter adapter) {
super(view, adapter);
ButterKnife.bind(this, view);
resources = view.getResources();
view.setOnClickListener(this);
}
void onBind(Subject item, List<GradesSubItem> subItems) {
subjectName.setText(item.getName());
numberText.setText(resources.getQuantityString(R.plurals.numberOfGradesPlurals,
subItems.size(), subItems.size()));
averageText.setText(getGradesAverageString(item));
alertImage.setVisibility(isSubItemsRead(subItems) ? View.INVISIBLE : View.VISIBLE);
alertImage.setTag(item.getName());
}
private boolean isSubItemsRead(List<GradesSubItem> subItems) {
boolean isRead = true;
for (GradesSubItem item : subItems) {
isRead = item.getGrade().getRead();
}
return isRead;
}
private String getGradesAverageString(Subject item) {
float average = AverageCalculator.calculate(item.getGradeList());
if (average < 0) {
return resources.getString(R.string.info_no_average);
} else {
return resources.getString(R.string.info_average_grades, average);
}
}
}
}

View File

@ -1,183 +0,0 @@
package io.github.wulkanowy.ui.main.grades;
import android.app.Activity;
import android.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.thoughtbot.expandablerecyclerview.ExpandableRecyclerViewAdapter;
import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder;
import com.thoughtbot.expandablerecyclerview.viewholders.GroupViewHolder;
import java.lang.ref.WeakReference;
import java.util.List;
import io.github.wulkanowy.R;
import io.github.wulkanowy.db.dao.entities.Grade;
import io.github.wulkanowy.utils.AverageCalculator;
public class GradesAdapter extends ExpandableRecyclerViewAdapter<GradesAdapter.SubjectViewHolder, GradesAdapter.GradeViewHolder> {
private static int numberOfNotReadGrade;
private Activity activity;
public GradesAdapter(List<? extends ExpandableGroup> groups, Activity activity) {
super(groups);
this.activity = activity;
}
@Override
public SubjectViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.subject_item, parent, false);
return new SubjectViewHolder(view, activity);
}
@Override
public GradeViewHolder onCreateChildViewHolder(ViewGroup child, int viewType) {
View view = LayoutInflater.from(child.getContext()).inflate(R.layout.grade_item, child, false);
return new GradeViewHolder(view, activity);
}
@Override
public void onBindGroupViewHolder(SubjectViewHolder holder, int flatPosition, ExpandableGroup group) {
holder.bind(group);
}
@Override
public void onBindChildViewHolder(GradeViewHolder holder, int flatPosition, ExpandableGroup group, int childIndex) {
holder.bind((Grade) group.getItems().get(childIndex));
}
public static class SubjectViewHolder extends GroupViewHolder {
private WeakReference<Activity> activity;
private TextView subjectName;
private TextView numberOfGrades;
private TextView averageGrades;
private ImageView subjectAlertNewGrades;
public SubjectViewHolder(View itemView, Activity activity) {
super(itemView);
this.activity = new WeakReference<>(activity);
subjectName = itemView.findViewById(R.id.subject_text);
numberOfGrades = itemView.findViewById(R.id.subject_number_of_grades);
subjectAlertNewGrades = itemView.findViewById(R.id.subject_new_grades_alert);
averageGrades = itemView.findViewById(R.id.subject_grades_average);
}
@SuppressWarnings("unchecked")
public void bind(ExpandableGroup group) {
int volumeGrades = group.getItemCount();
List<Grade> gradeList = group.getItems();
float average = AverageCalculator.calculate(gradeList);
itemView.setTag(group.getTitle());
if (average < 0) {
averageGrades.setText(R.string.info_no_average);
} else {
averageGrades.setText(activity.get().getResources().getString(R.string.info_average_grades, average));
}
subjectName.setText(group.getTitle());
numberOfGrades.setText(activity.get().getResources().getQuantityString(R.plurals.numberOfGradesPlurals, volumeGrades, volumeGrades));
for (Grade grade : gradeList) {
if (!grade.getRead()) {
subjectAlertNewGrades.setVisibility(View.VISIBLE);
} else {
subjectAlertNewGrades.setVisibility(View.INVISIBLE);
}
}
}
}
public static class GradeViewHolder extends ChildViewHolder {
private WeakReference<Activity> activity;
private TextView gradeValue;
private TextView descriptionGrade;
private TextView dateGrade;
private ImageView alertNewGrade;
private View itemView;
private Grade gradeItem;
public GradeViewHolder(View itemView, Activity activity) {
super(itemView);
this.itemView = itemView;
this.activity = new WeakReference<>(activity);
gradeValue = itemView.findViewById(R.id.grade_text);
descriptionGrade = itemView.findViewById(R.id.description_grade_text);
dateGrade = itemView.findViewById(R.id.grade_date_text);
alertNewGrade = itemView.findViewById(R.id.grade_new_grades_alert);
}
public void bind(Grade grade) {
gradeValue.setText(grade.getValue());
gradeValue.setBackgroundResource(grade.getValueColor());
dateGrade.setText(grade.getDate());
gradeItem = grade;
if (grade.getDescription() == null || "".equals(grade.getDescription())) {
if (!"".equals(grade.getSymbol())) {
descriptionGrade.setText(grade.getSymbol());
} else {
descriptionGrade.setText(R.string.noDescription_text);
}
} else {
descriptionGrade.setText(grade.getDescription());
}
if (gradeItem.getRead()) {
alertNewGrade.setVisibility(View.INVISIBLE);
} else {
alertNewGrade.setVisibility(View.VISIBLE);
numberOfNotReadGrade++;
}
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
GradesDialogFragment gradesDialogFragment = GradesDialogFragment.newInstance(gradeItem);
gradesDialogFragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0);
gradesDialogFragment.show(activity.get().getFragmentManager(), gradeItem.toString());
if (!gradeItem.getRead()) {
numberOfNotReadGrade--;
}
if (numberOfNotReadGrade == 0) {
View subjectView = activity.get().findViewById(R.id.subject_grade_recycler).findViewWithTag(gradeItem.getSubject());
if (subjectView != null) {
View subjectAlertNewGrade = subjectView.findViewById(R.id.subject_new_grades_alert);
subjectAlertNewGrade.setVisibility(View.INVISIBLE);
}
}
gradeItem.setRead(true);
gradeItem.setIsNew(false);
gradeItem.update();
alertNewGrade.setVisibility(View.INVISIBLE);
}
});
}
}
}

View File

@ -0,0 +1,40 @@
package io.github.wulkanowy.ui.main.grades;
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;
public interface GradesContract {
interface View extends BaseContract.View, SwipeRefreshLayout.OnRefreshListener {
void updateAdapterList(List<GradeHeaderItem> headerItems);
void showNoItem(boolean show);
void onRefreshSuccessNoGrade();
void onRefreshSuccess(int number);
void hideRefreshingBar();
void setActivityTitle();
boolean isMenuVisible();
}
@PerActivity
interface Presenter extends BaseContract.Presenter<View> {
void onFragmentVisible(boolean isVisible);
void onRefresh();
void onStart(View view, OnFragmentIsReadyListener listener);
}
}

View File

@ -1,107 +1,116 @@
package io.github.wulkanowy.ui.main.grades; package io.github.wulkanowy.ui.main.grades;
import android.app.Dialog;
import android.app.DialogFragment;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.db.dao.entities.Grade; import io.github.wulkanowy.data.db.dao.entities.Grade;
import io.github.wulkanowy.utils.CommonUtils;
public class GradesDialogFragment extends DialogFragment { public class GradesDialogFragment extends DialogFragment {
private static final String ARGUMENT_KEY = "Item";
private Grade grade; private Grade grade;
@BindView(R.id.grade_dialog_value)
TextView value;
@BindView(R.id.grade_dialog_subject)
TextView subject;
@BindView(R.id.grade_dialog_description_value)
TextView description;
@BindView(R.id.grade_dialog_weight_value)
TextView weight;
@BindView(R.id.grade_dialog_teacher_value)
TextView teacher;
@BindView(R.id.grade_dialog_color_value)
TextView color;
@BindView(R.id.grade_dialog_date_value)
TextView date;
public GradesDialogFragment() { public GradesDialogFragment() {
setRetainInstance(true); //empty constructor for fragment
} }
public static final GradesDialogFragment newInstance(Grade grade) { public static GradesDialogFragment newInstance(Grade item) {
return new GradesDialogFragment().setGrade(grade); GradesDialogFragment dialogFragment = new GradesDialogFragment();
Bundle bundle = new Bundle();
bundle.putSerializable(ARGUMENT_KEY, item);
dialogFragment.setArguments(bundle);
return dialogFragment;
} }
public static int colorHexToColorName(String hexColor) { @Override
switch (hexColor) { public void onCreate(@Nullable Bundle savedInstanceState) {
case "000000": super.onCreate(savedInstanceState);
return R.string.color_black_text; if (getArguments() != null) {
grade = (Grade) getArguments().getSerializable(ARGUMENT_KEY);
case "F04C4C":
return R.string.color_red_text;
case "20A4F7":
return R.string.color_blue_text;
case "6ECD07":
return R.string.color_green_text;
default:
return R.string.noColor_text;
} }
} }
private GradesDialogFragment setGrade(Grade grade) {
this.grade = grade;
return this;
}
@Nullable @Nullable
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.grades_dialog, container, false); View view = inflater.inflate(R.layout.grade_dialog, container, false);
TextView gradeText = view.findViewById(R.id.dialog_grade_text); ButterKnife.bind(this, view);
TextView subjectText = view.findViewById(R.id.subject_dialog_text_value);
TextView descriptionText = view.findViewById(R.id.description_dialog_text_value);
TextView weightText = view.findViewById(R.id.weight_dialog_text_value);
TextView teacherText = view.findViewById(R.id.teacher_dialog_text_value);
TextView dateText = view.findViewById(R.id.date_dialog_text_value);
TextView colorText = view.findViewById(R.id.color_dialog_text_value);
Button closeDialog = view.findViewById(R.id.close_dialog);
subjectText.setText(grade.getSubject()); subject.setText(grade.getSubject());
gradeText.setText(grade.getValue()); value.setText(grade.getValue());
gradeText.setBackgroundResource(grade.getValueColor()); value.setBackgroundResource(grade.getValueColor());
weightText.setText(grade.getWeight()); weight.setText(grade.getWeight());
dateText.setText(grade.getDate()); date.setText(grade.getDate());
colorText.setText(colorHexToColorName(grade.getColor())); color.setText(CommonUtils.colorHexToColorName(grade.getColor()));
teacher.setText(getTeacherString());
description.setText(getDescriptionString());
if ("".equals(grade.getDescription())) {
if (!"".equals(grade.getSymbol())) {
descriptionText.setText(grade.getSymbol());
}
} else if (!"".equals(grade.getSymbol())) {
descriptionText.setText(String.format("%1$s - %2$s", grade.getSymbol(), grade.getDescription()));
} else {
descriptionText.setText(grade.getDescription());
}
if (!"".equals(grade.getTeacher())) {
teacherText.setText(grade.getTeacher());
}
closeDialog.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
return view; return view;
} }
@Override @OnClick(R.id.grade_dialog_close_button)
public void onDestroyView() { void onClickClose() {
Dialog dialog = getDialog(); dismiss();
if (dialog != null && getRetainInstance()) { }
dialog.setDismissMessage(null);
private String getDescriptionString() {
if ("".equals(grade.getDescription())) {
if (!"".equals(grade.getSymbol())) {
return grade.getSymbol();
} else {
return getString(R.string.noDescription_text);
}
} else if (!"".equals(grade.getSymbol())) {
return String.format("%1$s - %2$s", grade.getSymbol(), grade.getDescription());
} else {
return grade.getDescription();
}
}
private String getTeacherString() {
if (grade.getTeacher() != null && !"".equals(grade.getTeacher())) {
return grade.getTeacher();
} else {
return getString(R.string.generic_app_no_data);
} }
super.onDestroyView();
} }
} }

View File

@ -1,211 +1,133 @@
package io.github.wulkanowy.ui.main.grades; package io.github.wulkanowy.ui.main.grades;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout; import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Toast;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List; 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.R;
import io.github.wulkanowy.WulkanowyApp; import io.github.wulkanowy.di.component.FragmentComponent;
import io.github.wulkanowy.api.Vulcan; import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.api.login.VulcanOfflineException; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.db.dao.DatabaseAccess;
import io.github.wulkanowy.db.dao.entities.Account;
import io.github.wulkanowy.db.dao.entities.AccountDao;
import io.github.wulkanowy.db.dao.entities.DaoSession;
import io.github.wulkanowy.db.dao.entities.Grade;
import io.github.wulkanowy.db.dao.entities.Subject;
import io.github.wulkanowy.services.jobs.VulcanJobHelper;
import io.github.wulkanowy.services.sync.LoginSession;
import io.github.wulkanowy.services.sync.VulcanSync;
import io.github.wulkanowy.utils.NetworkUtils;
public class GradesFragment extends Fragment { public class GradesFragment extends BaseFragment implements GradesContract.View {
private static List<SubjectWithGrades> subjectWithGradesList = new ArrayList<>(); @BindView(R.id.grade_fragment_recycler)
RecyclerView recyclerView;
private static long userId; @BindView(R.id.grade_fragment_no_item_container)
View noItemView;
@BindView(R.id.grade_fragment_swipe_refresh)
SwipeRefreshLayout refreshLayout;
@Inject
FlexibleAdapter<GradeHeaderItem> adapter;
@Inject
GradesContract.Presenter presenter;
public GradesFragment() { public GradesFragment() {
//empty constructor for fragments // empty constructor for fragment
}
private static void createExpList(View mainView, Activity activity) {
RecyclerView recyclerView = mainView.findViewById(R.id.subject_grade_recycler);
recyclerView.setLayoutManager(new LinearLayoutManager(activity));
GradesAdapter gradesAdapter = new GradesAdapter(subjectWithGradesList, activity);
recyclerView.setAdapter(gradesAdapter);
}
private static void downloadGradesFormDatabase(DaoSession daoSession) {
subjectWithGradesList = new ArrayList<>();
AccountDao accountDao = daoSession.getAccountDao();
Account account = accountDao.load(userId);
for (Subject subject : account.getSubjectList()) {
List<Grade> gradeList = subject.getGradeList();
if (!gradeList.isEmpty()) {
SubjectWithGrades subjectWithGrades = new SubjectWithGrades(subject.getName(), gradeList);
subjectWithGradesList.add(subjectWithGrades);
}
}
} }
@Nullable
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_grades, container, false); View view = inflater.inflate(R.layout.fragment_grades, container, false);
view.findViewById(R.id.fragment_no_grades).setVisibility(View.GONE);
if (getActivity() != null) { FragmentComponent component = getFragmentComponent();
DaoSession daoSession = ((WulkanowyApp) getActivity().getApplication()).getDaoSession(); if (component != null) {
userId = getActivity().getSharedPreferences("LoginData", Context.MODE_PRIVATE) component.inject(this);
.getLong("userId", 0); setButterKnife(ButterKnife.bind(this, view));
presenter.onStart(this, (OnFragmentIsReadyListener) getActivity());
prepareRefreshLayout(view, daoSession);
if (subjectWithGradesList.equals(new ArrayList<>())) {
createExpList(view, getActivity());
new GenerateListTask(getActivity(), view, daoSession).execute();
} else {
createExpList(view, getActivity());
view.findViewById(R.id.loadingPanel).setVisibility(View.INVISIBLE);
}
} }
return view; return view;
} }
private void prepareRefreshLayout(final View mainView, final DaoSession daoSession) {
final SwipeRefreshLayout swipeRefreshLayout = mainView.findViewById(R.id.grade_swipe_refresh);
swipeRefreshLayout.setColorSchemeResources(android.R.color.black,
android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
android.R.color.holo_red_light);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override @Override
public void onRefresh() { protected void setUpOnViewCreated(View fragmentView) {
if (NetworkUtils.isOnline(getContext())) { noItemView.setVisibility(View.GONE);
new RefreshTask(getActivity(), mainView, daoSession).execute();
} else { adapter.setAutoCollapseOnExpand(true);
Toast.makeText(mainView.getContext(), R.string.noInternet_text, Toast.LENGTH_SHORT).show(); adapter.setAutoScrollOnExpand(true);
swipeRefreshLayout.setRefreshing(false); adapter.expandItemsAtStartUp();
}
} recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext()));
}); recyclerView.setAdapter(adapter);
refreshLayout.setColorSchemeResources(android.R.color.black);
refreshLayout.setOnRefreshListener(this);
} }
private static class GenerateListTask extends AsyncTask<Void, Void, Void> { @Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
if (presenter != null) {
presenter.onFragmentVisible(menuVisible);
}
}
private WeakReference<View> mainView; @Override
public void setActivityTitle() {
private WeakReference<Activity> activity; setTitle(getString(R.string.grades_text));
private DaoSession daoSession;
public GenerateListTask(Activity activity, View mainView, DaoSession daoSession) {
this.activity = new WeakReference<>(activity);
this.mainView = new WeakReference<>(mainView);
this.daoSession = daoSession;
} }
@Override @Override
protected Void doInBackground(Void... params) { public void onRefresh() {
downloadGradesFormDatabase(daoSession); presenter.onRefresh();
return null;
} }
@Override @Override
protected void onPostExecute(Void result) { public void showNoItem(boolean show) {
super.onPostExecute(result); noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
createExpList(mainView.get(), activity.get());
mainView.get().findViewById(R.id.loadingPanel).setVisibility(View.INVISIBLE);
if (subjectWithGradesList.isEmpty()) {
mainView.get().findViewById(R.id.fragment_no_grades).setVisibility(View.VISIBLE);
}
}
}
private static class RefreshTask extends AsyncTask<Void, Void, Integer> {
private DaoSession daoSession;
private WeakReference<Activity> activity;
private WeakReference<View> mainView;
public RefreshTask(Activity activity, View mainView, DaoSession daoSession) {
this.activity = new WeakReference<>(activity);
this.daoSession = daoSession;
this.mainView = new WeakReference<>(mainView);
} }
@Override @Override
protected Integer doInBackground(Void... params) { public void hideRefreshingBar() {
VulcanSync vulcanSync = new VulcanSync(new LoginSession()); refreshLayout.setRefreshing(false);
try { }
vulcanSync.loginCurrentUser(activity.get(), daoSession, new Vulcan());
vulcanSync.syncGrades(); @Override
downloadGradesFormDatabase(daoSession); public void updateAdapterList(List<GradeHeaderItem> headerItems) {
return 1; adapter.updateDataSet(headerItems);
} catch (VulcanOfflineException e) { }
Log.e(VulcanJobHelper.DEBUG_TAG, "There was a sync problem, because vulcan is offline", e);
return R.string.error_host_offline; @Override
} catch (Exception e) { public void onRefreshSuccessNoGrade() {
Log.e(VulcanJobHelper.DEBUG_TAG, "There was a sync problem", e); onError(R.string.snackbar_no_grades);
return R.string.refresh_error_text; }
@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();
} }
} }
@Override @Override
protected void onPostExecute(Integer messageID) { public void onDestroyView() {
super.onPostExecute(messageID); presenter.onDestroy();
super.onDestroyView();
if (1 == messageID) {
if (mainView.get() != null && activity.get() != null) {
createExpList(mainView.get(), activity.get());
}
int volumeGrades = new DatabaseAccess().getNewGrades(daoSession).size();
if (volumeGrades == 0) {
Snackbar.make(activity.get().findViewById(R.id.fragment_container),
R.string.snackbar_no_grades,
Snackbar.LENGTH_SHORT).show();
} else {
Snackbar.make(activity.get().findViewById(R.id.fragment_container),
activity.get().getString(R.string.snackbar_new_grade, volumeGrades),
Snackbar.LENGTH_SHORT).show();
}
} else {
Toast.makeText(activity.get(), messageID, Toast.LENGTH_SHORT).show();
}
if (mainView.get() != null) {
SwipeRefreshLayout swipeRefreshLayout = mainView.get().findViewById(R.id.grade_swipe_refresh);
swipeRefreshLayout.setRefreshing(false);
}
}
} }
} }

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