Compare commits

..

No commits in common. "0.3.0" and "0.2.1" have entirely different histories.
0.3.0 ... 0.2.1

74 changed files with 365 additions and 699 deletions

View file

@ -7,7 +7,7 @@ references:
container_config: &container_config container_config: &container_config
docker: docker:
- image: circleci/android:api-26-alpha - image: circleci/android:api-27-alpha
working_directory: *workspace_root working_directory: *workspace_root
environment: environment:
environment: environment:

2
.gitignore vendored
View file

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

View file

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

View file

@ -4,10 +4,9 @@
[![Bitrise](https://img.shields.io/bitrise/daeff1893f3c8128/master.svg?token=Hjm1ACamk86JDeVVJHOeqQ&style=flat-square)](https://www.bitrise.io/app/daeff1893f3c8128) [![Bitrise](https://img.shields.io/bitrise/daeff1893f3c8128/master.svg?token=Hjm1ACamk86JDeVVJHOeqQ&style=flat-square)](https://www.bitrise.io/app/daeff1893f3c8128)
[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy)
[![BCH compliance](https://bettercodehub.com/edge/badge/wulkanowy/wulkanowy?branch=master)](https://bettercodehub.com/) [![BCH compliance](https://bettercodehub.com/edge/badge/wulkanowy/wulkanowy?branch=master)](https://bettercodehub.com/)
[![Scrutinizer](https://img.shields.io/scrutinizer/g/wulkanowy/wulkanowy.svg)](https://scrutinizer-ci.com/g/wulkanowy/wulkanowy/?branch=master)
[![Known Vulnerabilities](https://snyk.io/test/github/wulkanowy/wulkanowy/badge.svg?targetFile=app%2Fbuild.gradle&style=flat-square)](https://snyk.io/test/github/wulkanowy/wulkanowy?targetFile=app%2Fbuild.gradle) [![Known Vulnerabilities](https://snyk.io/test/github/wulkanowy/wulkanowy/badge.svg?targetFile=app%2Fbuild.gradle&style=flat-square)](https://snyk.io/test/github/wulkanowy/wulkanowy?targetFile=app%2Fbuild.gradle)
[![Bintray](https://img.shields.io/bintray/v/wulkanowy/wulkanowy/api.svg?style=flat-square)](https://bintray.com/wulkanowy/wulkanowy/api) [![Bintray](https://img.shields.io/bintray/v/wulkanowy/wulkanowy/api.svg?style=flat-square)](https://bintray.com/wulkanowy/wulkanowy/api)
[Pobierz wersję rozwojową](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/app-debug-bitrise-signed.apk) [Pobierz wersję rozwojową](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/app-debug-bitrise-signed.apk)
Androidowy klient dziennika VULCAN UONET+. Wulkanowy to aplikacja na androida polepszająca wygodę używania dziennika UONET+.

View file

@ -5,7 +5,7 @@ sonarqube {
//noinspection GroovyAssignabilityCheck //noinspection GroovyAssignabilityCheck
properties { properties {
def files = fileTree("${rootProject.projectDir}/api/build/libs/").filter { it.isFile() }.files.name def files = fileTree("${rootProject.projectDir}/api/build/libs/").filter { it.isFile() }.files.name
def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-26/android.jar," + def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-27/android.jar," +
"${project.rootDir}/api/build/libs/" + files[0] "${project.rootDir}/api/build/libs/" + files[0]
property "sonar.projectName", GROUP_ID + ":app" property "sonar.projectName", GROUP_ID + ":app"

View file

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

View file

@ -108,11 +108,7 @@ public class Client {
this.cookies.addItems(response.cookies()); this.cookies.addItems(response.cookies());
Document doc = checkForErrors(response.parse()); return checkForErrors(response.parse());
lastSuccessRequest = new Date();
return doc;
} }
public Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException { public Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException {
@ -169,16 +165,16 @@ public class Client {
} }
Document checkForErrors(Document doc) throws VulcanException { Document checkForErrors(Document doc) throws VulcanException {
String title = doc.select("title").text(); if ("Przerwa techniczna".equals(doc.select("title").text())) {
if ("Przerwa techniczna".equals(title)) { throw new VulcanOfflineException();
throw new VulcanOfflineException(title);
} }
String singIn = doc.select(".loginButton").text(); if ("Zaloguj się".equals(doc.select(".loginButton").text())) {
if ("Zaloguj się".equals(singIn)) { throw new NotLoggedInErrorException();
throw new NotLoggedInErrorException(singIn);
} }
lastSuccessRequest = new Date();
return doc; return doc;
} }
} }

View file

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

View file

@ -48,7 +48,7 @@ public class StudentAndParent implements SnP {
Element studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a").first(); Element studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a").first();
if (null == studentTileLink) { if (null == studentTileLink) {
throw new NotLoggedInErrorException("You are probably not logged in. Force login"); throw new NotLoggedInErrorException();
} }
String snpPageUrl = studentTileLink.attr("href"); String snpPageUrl = studentTileLink.attr("href");
@ -62,7 +62,7 @@ public class StudentAndParent implements SnP {
String[] path = snpPageUrl.split(client.getHost())[1].split("/"); String[] path = snpPageUrl.split(client.getHost())[1].split("/");
if (5 != path.length) { if (5 != path.length) {
throw new NotLoggedInErrorException("You are probably not logged in"); throw new NotLoggedInErrorException();
} }
return path[2]; return path[2];

View file

@ -32,7 +32,7 @@ public class Vulcan {
public Client getClient() throws NotLoggedInErrorException { public Client getClient() throws NotLoggedInErrorException {
if (null == client) { if (null == client) {
throw new NotLoggedInErrorException("Use setCredentials() method first"); throw new NotLoggedInErrorException();
} }
return client; return client;

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,6 @@ package io.github.wulkanowy.api.login;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser; import org.jsoup.parser.Parser;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
@ -43,9 +42,8 @@ public class Login {
{"Password", password} {"Password", password}
}); });
Element errorMessage = html.select(".ErrorMessage").first(); if (null != html.select(".ErrorMessage").first()) {
if (null != errorMessage) { throw new BadCredentialsException();
throw new BadCredentialsException(errorMessage.text());
} }
return html.select("input[name=wresult]").attr("value"); return html.select("input[name=wresult]").attr("value");
@ -61,11 +59,11 @@ public class Login {
}).select("title").text(); }).select("title").text();
if ("Logowanie".equals(title)) { if ("Logowanie".equals(title)) {
throw new AccountPermissionException("No account access. Try another symbol"); throw new AccountPermissionException();
} }
if (!"Uonet+".equals(title)) { if (!"Uonet+".equals(title)) {
throw new LoginErrorException("Could not log in, unknown error"); throw new LoginErrorException();
} }
return this.symbol; return this.symbol;

View file

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

View file

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

View file

@ -59,10 +59,10 @@ public class Messages {
messages = new Gson().fromJson(res, MessagesContainer.class).data; messages = new Gson().fromJson(res, MessagesContainer.class).data;
} catch (JsonParseException e) { } catch (JsonParseException e) {
if (res.contains(ERROR_TITLE)) { if (res.contains(ERROR_TITLE)) {
throw new BadRequestException(ERROR_TITLE); throw new BadRequestException();
} }
throw new NotLoggedInErrorException("You are probably not logged in"); throw new NotLoggedInErrorException();
} }
return messages; return messages;
@ -80,10 +80,10 @@ public class Messages {
message = new Gson().fromJson(res, MessageContainer.class).data; message = new Gson().fromJson(res, MessageContainer.class).data;
} catch (JsonParseException e) { } catch (JsonParseException e) {
if (res.contains(ERROR_TITLE)) { if (res.contains(ERROR_TITLE)) {
throw new BadRequestException(ERROR_TITLE); throw new BadRequestException();
} }
throw new NotLoggedInErrorException("You are probably not logged in. Force login"); throw new NotLoggedInErrorException();
} }
return message; return message;

View file

@ -21,15 +21,15 @@ apply from: '../jacoco.gradle'
apply from: '../android-sonarqube.gradle' apply from: '../android-sonarqube.gradle'
android { android {
compileSdkVersion 26 compileSdkVersion 27
buildToolsVersion '27.0.3' buildToolsVersion "27.0.3"
defaultConfig { defaultConfig {
applicationId "io.github.wulkanowy" applicationId "io.github.wulkanowy"
testApplicationId "io.github.tests.wulkanowy" testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 26 targetSdkVersion 27
versionCode 6 versionCode 4
versionName "0.3.0" versionName "0.2.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
manifestPlaceholders = [ manifestPlaceholders = [
@ -68,40 +68,42 @@ greendao {
dependencies { dependencies {
implementation project(':api') implementation project(':api')
implementation "com.android.support:support-v4:$supportVersion" implementation 'com.android.support:appcompat-v7:27.1.0'
implementation "com.android.support:design:$supportVersion" implementation 'com.android.support:design:27.1.0'
implementation "com.android.support:cardview-v7:$supportVersion" implementation 'com.android.support:support-v4:27.1.0'
implementation "com.android.support:customtabs:$supportVersion" implementation 'com.android.support:recyclerview-v7:27.1.0'
implementation "com.android.support:preference-v14:$supportVersion" implementation 'com.android.support:cardview-v7:27.1.0'
implementation "com.firebase:firebase-jobdispatcher:$firebaseJob" implementation 'com.android.support:customtabs:27.1.0'
implementation "org.apache.commons:commons-lang3:$apacheLang" implementation 'com.firebase:firebase-jobdispatcher:0.8.5'
implementation "org.apache.commons:commons-collections4:$apacheCollections" implementation 'org.apache.commons:commons-lang3:3.7'
implementation "eu.davidea:flexible-adapter:$flexibleAdapter" implementation 'eu.davidea:flexible-adapter:5.0.0-rc4'
implementation "eu.davidea:flexible-adapter-ui:$flexibleUi" implementation 'eu.davidea:flexible-adapter-ui:1.0.0-b1'
implementation "org.greenrobot:greendao:$greenDao" implementation 'org.apache.commons:commons-collections4:4.1'
implementation "com.github.yuweiguocn:GreenDaoUpgradeHelper:$greenDaoHelper" implementation 'org.greenrobot:greendao:3.2.2'
implementation "com.jakewharton:butterknife:$butterknife" implementation 'com.github.yuweiguocn:GreenDaoUpgradeHelper:v2.0.2'
implementation "com.google.dagger:dagger-android-support:$dagger2" implementation 'com.jakewharton:butterknife:8.8.1'
implementation "com.aurelhubert:ahbottomnavigation:$ahbottom" implementation 'joda-time:joda-time:2.9.9'
implementation "com.jakewharton.threetenabp:threetenabp:$threeTenABP" implementation 'com.google.dagger:dagger-android:2.14.1'
implementation 'com.google.dagger:dagger-android-support:2.14.1'
implementation 'com.aurelhubert:ahbottomnavigation:2.1.0'
implementation("com.crashlytics.sdk.android:crashlytics:$crashlyticsSdk@aar") { implementation('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {
transitive = true transitive = true
} }
implementation("com.crashlytics.sdk.android:answers:$crashlyticsAnswers@aar") { implementation('com.crashlytics.sdk.android:answers:1.4.1@aar') {
transitive = true transitive = true
} }
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger2" annotationProcessor 'com.google.dagger:dagger-android-processor:2.14.1'
annotationProcessor "com.google.dagger:dagger-compiler:$dagger2" annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'
annotationProcessor "com.jakewharton:butterknife-compiler:$butterknife" annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
debugImplementation "com.amitshekhar.android:debug-db:$debugDb" debugImplementation 'com.amitshekhar.android:debug-db:1.0.1'
debugImplementation "net.zetetic:android-database-sqlcipher:$sqlcipher" debugImplementation 'net.zetetic:android-database-sqlcipher:3.5.9'
testImplementation "junit:junit:$junit" testImplementation 'junit:junit:4.12'
testImplementation "org.mockito:mockito-core:$mockito" testImplementation 'org.mockito:mockito-core:2.13.0'
androidTestImplementation "com.android.support.test:runner:$testRunner" androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation "org.mockito:mockito-android:$mockito" androidTestImplementation 'org.mockito:mockito-android:2.13.0'
} }

View file

@ -4,7 +4,6 @@ import android.app.Application;
import com.crashlytics.android.Crashlytics; import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.core.CrashlyticsCore; import com.crashlytics.android.core.CrashlyticsCore;
import com.jakewharton.threetenabp.AndroidThreeTen;
import org.greenrobot.greendao.query.QueryBuilder; import org.greenrobot.greendao.query.QueryBuilder;
@ -29,8 +28,6 @@ public class WulkanowyApp extends Application {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
AndroidThreeTen.init(this);
applicationComponent = DaggerApplicationComponent applicationComponent = DaggerApplicationComponent
.builder() .builder()
.applicationModule(new ApplicationModule(this)) .applicationModule(new ApplicationModule(this))

View file

@ -68,31 +68,6 @@ public class Repository implements RepositoryContract {
return sharedPref.getCurrentUserId(); return sharedPref.getCurrentUserId();
} }
@Override
public int getStartupTab() {
return sharedPref.getStartupTab();
}
@Override
public int getServicesInterval() {
return sharedPref.getServicesInterval();
}
@Override
public boolean isServicesEnable() {
return sharedPref.isServicesEnable();
}
@Override
public boolean isNotifyEnable() {
return sharedPref.isNotifyEnable();
}
@Override
public boolean isMobileDisable() {
return sharedPref.isMobileDisable();
}
@Override @Override
public String[] getSymbolsKeysArray() { public String[] getSymbolsKeysArray() {
return resources.getSymbolsKeysArray(); return resources.getSymbolsKeysArray();

View file

@ -21,16 +21,6 @@ public interface RepositoryContract extends ResourcesContract, AccountSyncContra
long getCurrentUserId(); long getCurrentUserId();
int getStartupTab();
boolean isServicesEnable();
boolean isNotifyEnable();
int getServicesInterval();
boolean isMobileDisable();
void syncGrades() throws VulcanException, IOException, ParseException; void syncGrades() throws VulcanException, IOException, ParseException;
void syncSubjects() throws VulcanException, IOException, ParseException; void syncSubjects() throws VulcanException, IOException, ParseException;

View file

@ -3,8 +3,6 @@ package io.github.wulkanowy.data.db.resources;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import com.crashlytics.android.Crashlytics;
import java.io.IOException; import java.io.IOException;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
@ -14,6 +12,7 @@ import javax.inject.Singleton;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.api.NotLoggedInErrorException; import io.github.wulkanowy.api.NotLoggedInErrorException;
import io.github.wulkanowy.api.VulcanOfflineException;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.di.annotations.ApplicationContext; import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.utils.AppConstant; import io.github.wulkanowy.utils.AppConstant;
@ -43,7 +42,6 @@ public class AppResources implements ResourcesContract {
@Override @Override
public String getErrorLoginMessage(Exception exception) { public String getErrorLoginMessage(Exception exception) {
LogUtils.error(AppConstant.APP_NAME + " encountered a error", exception); LogUtils.error(AppConstant.APP_NAME + " encountered a error", exception);
Crashlytics.logException(exception);
if (exception instanceof CryptoException) { if (exception instanceof CryptoException) {
return resources.getString(R.string.encrypt_failed_text); return resources.getString(R.string.encrypt_failed_text);
@ -53,6 +51,8 @@ public class AppResources implements ResourcesContract {
return resources.getString(R.string.generic_timeout_error); return resources.getString(R.string.generic_timeout_error);
} else if (exception instanceof NotLoggedInErrorException || exception instanceof IOException) { } else if (exception instanceof NotLoggedInErrorException || exception instanceof IOException) {
return resources.getString(R.string.login_denied_text); return resources.getString(R.string.login_denied_text);
} else if (exception instanceof VulcanOfflineException) {
return resources.getString(R.string.error_host_offline);
} else { } else {
return exception.getMessage(); return exception.getMessage();
} }
@ -67,23 +67,23 @@ public class AppResources implements ResourcesContract {
} }
if (lesson.getIsAbsenceExcused()) { if (lesson.getIsAbsenceExcused()) {
id = R.string.attendance_absence_excused; id = R.string.attendance_absence_excused;
} }
if (lesson.getIsAbsenceUnexcused()) { if (lesson.getIsAbsenceUnexcused()) {
id = R.string.attendance_absence_unexcused; id = R.string.attendance_absence_unexcused;
} }
if (lesson.getIsExemption()) { if (lesson.getIsExemption()) {
id = R.string.attendance_exemption; id = R.string.attendance_exemption;
} }
if (lesson.getIsExcusedLateness()) { if (lesson.getIsExcusedLateness()) {
id = R.string.attendance_excused_lateness; id = R.string.attendance_excused_lateness;
} }
if (lesson.getIsUnexcusedLateness()) { if (lesson.getIsUnexcusedLateness()) {
id = R.string.attendance_unexcused_lateness; id = R.string.attendance_unexcused_lateness;
} }
return resources.getString(id); return resources.getString(id);

View file

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

View file

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

View file

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

View file

@ -3,22 +3,29 @@ package io.github.wulkanowy.services;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.support.annotation.StringRes;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import java.util.Random; import java.util.Random;
public class NotificationService { 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 NotificationManager manager;
private Context context; private Context context;
public NotificationService(Context context) { NotificationService(Context context) {
this.context = context; this.context = context;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannel();
}
} }
void notify(Notification notification) { void notify(Notification notification) {
@ -26,32 +33,23 @@ public class NotificationService {
} }
NotificationCompat.Builder notificationBuilder() { NotificationCompat.Builder notificationBuilder() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { return new NotificationCompat.Builder(context, CHANNEL_ID);
createChannel();
}
return new NotificationCompat.Builder(context, getChannelId());
} }
public void cancelAll() { @TargetApi(26)
getManager().cancelAll(); 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);
} }
NotificationManager getManager() { private NotificationManager getManager() {
if (manager == null) { if (manager == null) {
manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
} }
return manager; return manager;
} }
}
String getString(@StringRes int stringId) {
return context.getString(stringId);
}
@TargetApi(26)
void createChannel() {
}
String getChannelId() {
return null;
}
}

View file

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

View file

@ -7,7 +7,6 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.design.widget.TextInputLayout; import android.support.design.widget.TextInputLayout;
import android.support.v7.app.ActionBar;
import android.view.View; import android.view.View;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
@ -210,18 +209,6 @@ public class LoginActivity extends BaseActivity implements LoginContract.View {
}); });
} }
@Override
public void showActionBar(boolean show) {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
if (show) {
actionBar.show();
} else {
actionBar.hide();
}
}
}
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();

View file

@ -33,8 +33,6 @@ public interface LoginContract {
void hideSoftInput(); void hideSoftInput();
void showActionBar(boolean show);
} }
@PerActivity @PerActivity

View file

@ -55,7 +55,6 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
@Override @Override
public void onStartAsync() { public void onStartAsync() {
if (isViewAttached()) { if (isViewAttached()) {
getView().showActionBar(false);
getView().showLoginProgress(true); getView().showLoginProgress(true);
} }
} }
@ -85,25 +84,24 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
public void onEndAsync(boolean success, Exception exception) { public void onEndAsync(boolean success, Exception exception) {
if (success) { if (success) {
getView().openMainActivity(); getView().openMainActivity();
return;
} else if (exception instanceof BadCredentialsException) { } else if (exception instanceof BadCredentialsException) {
getView().setErrorPassIncorrect(); getView().setErrorPassIncorrect();
getView().showSoftInput(); getView().showSoftInput();
getView().showLoginProgress(false);
} else if (exception instanceof AccountPermissionException) { } else if (exception instanceof AccountPermissionException) {
getView().setErrorSymbolRequired(); getView().setErrorSymbolRequired();
getView().showSoftInput(); getView().showSoftInput();
getView().showLoginProgress(false);
} else { } else {
getView().onError(getRepository().getErrorLoginMessage(exception)); getView().onError(getRepository().getErrorLoginMessage(exception));
getView().showLoginProgress(false);
} }
getView().showActionBar(true);
getView().showLoginProgress(false);
} }
@Override @Override
public void onCanceledAsync() { public void onCanceledAsync() {
if (isViewAttached()) { if (isViewAttached()) {
getView().showActionBar(true);
getView().showLoginProgress(false); getView().showLoginProgress(false);
} }
} }

View file

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

View file

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

View file

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

View file

@ -17,16 +17,20 @@ public interface AttendanceContract {
void setAdapterWithTabLayout(); void setAdapterWithTabLayout();
void setChildFragmentSelected(int position, boolean selected);
boolean isMenuVisible(); boolean isMenuVisible();
} }
@PerActivity @PerActivity
interface Presenter extends BaseContract.Presenter<View> { interface Presenter extends BaseContract.Presenter<View> {
void onFragmentActivated(boolean isVisible); void onFragmentVisible(boolean isVisible);
void onTabSelected(int position);
void onTabUnselected(int position);
void onStart(View view, OnFragmentIsReadyListener listener); void onStart(View view, OnFragmentIsReadyListener listener);
void setRestoredPosition(int position);
} }
} }

View file

@ -20,9 +20,7 @@ import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.TabsData; import io.github.wulkanowy.ui.main.TabsData;
public class AttendanceFragment extends BaseFragment implements AttendanceContract.View { public class AttendanceFragment extends BaseFragment implements AttendanceContract.View, TabLayout.OnTabSelectedListener {
private static final String CURRENT_ITEM_KEY = "CurrentItem";
@BindView(R.id.attendance_fragment_viewpager) @BindView(R.id.attendance_fragment_viewpager)
ViewPager viewPager; ViewPager viewPager;
@ -47,10 +45,6 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
component.inject(this); component.inject(this);
setButterKnife(ButterKnife.bind(this, view)); setButterKnife(ButterKnife.bind(this, view));
presenter.onStart(this, (OnFragmentIsReadyListener) getActivity()); presenter.onStart(this, (OnFragmentIsReadyListener) getActivity());
if (savedInstanceState != null) {
presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY));
}
} }
return view; return view;
@ -60,10 +54,25 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
public void setMenuVisibility(boolean menuVisible) { public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible); super.setMenuVisibility(menuVisible);
if (presenter != null) { if (presenter != null) {
presenter.onFragmentActivated(menuVisible); presenter.onFragmentVisible(menuVisible);
} }
} }
@Override
public void onTabSelected(TabLayout.Tab tab) {
presenter.onTabSelected(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
presenter.onTabUnselected(tab.getPosition());
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
//do nothing
}
@Override @Override
public void setTabDataToAdapter(TabsData tabsData) { public void setTabDataToAdapter(TabsData tabsData) {
pagerAdapter.setTabsData(tabsData); pagerAdapter.setTabsData(tabsData);
@ -72,7 +81,14 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
@Override @Override
public void setAdapterWithTabLayout() { public void setAdapterWithTabLayout() {
viewPager.setAdapter(pagerAdapter); viewPager.setAdapter(pagerAdapter);
tabLayout.setupWithViewPager(viewPager); tabLayout.setupWithViewPager(viewPager);
tabLayout.addOnTabSelectedListener(this);
}
@Override
public void setChildFragmentSelected(int position, boolean selected) {
((AttendanceTabFragment) pagerAdapter.getItem(position)).setSelected(selected);
} }
@Override @Override
@ -93,12 +109,6 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
} }
} }
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem());
super.onSaveInstanceState(outState);
}
@Override @Override
public void onDestroyView() { public void onDestroyView() {
presenter.onDestroy(); presenter.onDestroy();

View file

@ -19,7 +19,6 @@ import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem; import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem;
import eu.davidea.flexibleadapter.items.IFlexible;
import eu.davidea.viewholders.ExpandableViewHolder; import eu.davidea.viewholders.ExpandableViewHolder;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.Day; import io.github.wulkanowy.data.db.dao.entities.Day;
@ -59,13 +58,12 @@ public class AttendanceHeaderItem
} }
@Override @Override
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) { public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
return new HeaderViewHolder(view, adapter); return new HeaderViewHolder(view, adapter);
} }
@Override @Override
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, HeaderViewHolder holder, public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List payloads) {
int position, List<Object> payloads) {
holder.onBind(day, getSubItems()); holder.onBind(day, getSubItems());
} }

View file

@ -24,7 +24,7 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
private OnFragmentIsReadyListener listener; private OnFragmentIsReadyListener listener;
private int positionToScroll = 0; private int positionToScroll;
private boolean isFirstSight = false; private boolean isFirstSight = false;
@ -45,10 +45,7 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
if (dates.isEmpty()) { if (dates.isEmpty()) {
dates = TimeUtils.getMondaysFromCurrentSchoolYear(); dates = TimeUtils.getMondaysFromCurrentSchoolYear();
} }
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
if (positionToScroll == 0) {
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
}
if (!isFirstSight) { if (!isFirstSight) {
isFirstSight = true; isFirstSight = true;
@ -60,12 +57,22 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
} }
@Override @Override
public void onFragmentActivated(boolean isVisible) { public void onFragmentVisible(boolean isVisible) {
if (isVisible) { if (isVisible) {
getView().setActivityTitle(); getView().setActivityTitle();
} }
} }
@Override
public void onTabSelected(int position) {
getView().setChildFragmentSelected(position, true);
}
@Override
public void onTabUnselected(int position) {
getView().setChildFragmentSelected(position, false);
}
@Override @Override
public void onDoInBackgroundLoading() throws Exception { public void onDoInBackgroundLoading() throws Exception {
for (String date : dates) { for (String date : dates) {
@ -90,11 +97,6 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
} }
} }
@Override
public void setRestoredPosition(int position) {
this.positionToScroll = position;
}
@Override @Override
public void onDestroy() { public void onDestroy() {
isFirstSight = false; isFirstSight = false;

View file

@ -16,7 +16,6 @@ import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractSectionableItem; import eu.davidea.flexibleadapter.items.AbstractSectionableItem;
import eu.davidea.flexibleadapter.items.IFlexible;
import eu.davidea.viewholders.FlexibleViewHolder; import eu.davidea.viewholders.FlexibleViewHolder;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
@ -61,13 +60,12 @@ class AttendanceSubItem
} }
@Override @Override
public SubItemViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) { public AttendanceSubItem.SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
return new SubItemViewHolder(view, adapter); return new AttendanceSubItem.SubItemViewHolder(view, adapter);
} }
@Override @Override
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, SubItemViewHolder holder, public void bindViewHolder(FlexibleAdapter adapter, AttendanceSubItem.SubItemViewHolder holder, int position, List payloads) {
int position, List<Object> payloads) {
holder.onBind(lesson); holder.onBind(lesson);
} }

View file

@ -21,10 +21,12 @@ public interface AttendanceTabContract {
interface Presenter extends BaseContract.Presenter<AttendanceTabContract.View> { interface Presenter extends BaseContract.Presenter<AttendanceTabContract.View> {
void onFragmentActivated(boolean isSelected); void onFragmentSelected(boolean isSelected);
void setArgumentDate(String date); void setArgumentDate(String date);
void onStart(AttendanceTabContract.View view, boolean isPrimary);
void onRefresh(); void onRefresh();
} }
} }

View file

@ -27,6 +27,12 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
private static final String ARGUMENT_KEY = "date"; private static final String ARGUMENT_KEY = "date";
private static final String SAVED_KEY = "isSelected";
private boolean isPrimary = false;
private boolean isSelected = false;
@BindView(R.id.attendance_tab_fragment_recycler) @BindView(R.id.attendance_tab_fragment_recycler)
RecyclerView recyclerView; RecyclerView recyclerView;
@ -45,8 +51,6 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
@Inject @Inject
FlexibleAdapter<AttendanceHeaderItem> adapter; FlexibleAdapter<AttendanceHeaderItem> adapter;
private boolean isFragmentVisible = false;
public static AttendanceTabFragment newInstance(String date) { public static AttendanceTabFragment newInstance(String date) {
AttendanceTabFragment fragmentTab = new AttendanceTabFragment(); AttendanceTabFragment fragmentTab = new AttendanceTabFragment();
@ -57,6 +61,15 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
return fragmentTab; return fragmentTab;
} }
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
isSelected = savedInstanceState.getBoolean(SAVED_KEY, isSelected);
}
}
@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) {
@ -71,8 +84,7 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY)); presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
} }
presenter.onStart(this); presenter.onStart(this, isPrimary);
presenter.onFragmentActivated(isFragmentVisible);
} }
return view; return view;
} }
@ -99,9 +111,10 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
@Override @Override
public void setMenuVisibility(boolean menuVisible) { public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible); super.setMenuVisibility(menuVisible);
isFragmentVisible = menuVisible; if (presenter != null && getView() != null) {
if (presenter != null) { presenter.onFragmentSelected(isSelected);
presenter.onFragmentActivated(menuVisible); } else if (isSelected) {
isPrimary = true;
} }
} }
@ -130,6 +143,10 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
} }
public void setSelected(boolean selected) {
isSelected = selected;
}
@Override @Override
public void onError(String message) { public void onError(String message) {
if (getActivity() != null) { if (getActivity() != null) {
@ -138,8 +155,15 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
} }
} }
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(SAVED_KEY, isSelected);
super.onSaveInstanceState(outState);
}
@Override @Override
public void onDestroyView() { public void onDestroyView() {
isPrimary = false;
presenter.onDestroy(); presenter.onDestroy();
super.onDestroyView(); super.onDestroyView();
} }

View file

@ -33,22 +33,21 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
} }
@Override @Override
public void onStart(AttendanceTabContract.View view) { public void onStart(AttendanceTabContract.View view, boolean isPrimary) {
super.onStart(view); super.onStart(view);
getView().showProgressBar(true); getView().showProgressBar(true);
getView().showNoItem(false); getView().showNoItem(false);
onFragmentSelected(isPrimary);
} }
@Override @Override
public void onFragmentActivated(boolean isSelected) { public void onFragmentSelected(boolean isSelected) {
if (!isFirstSight && isSelected && isViewAttached()) { if (!isFirstSight && isSelected) {
isFirstSight = true; isFirstSight = true;
loadingTask = new AbstractTask(); loadingTask = new AbstractTask();
loadingTask.setOnFirstLoadingListener(this); loadingTask.setOnFirstLoadingListener(this);
loadingTask.execute(); loadingTask.execute();
} else if (!isSelected) {
cancelAsyncTasks();
} }
} }
@ -158,7 +157,10 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
getRepository().syncAttendance(date); getRepository().syncAttendance(date);
} }
private void cancelAsyncTasks() { @Override
public void onDestroy() {
isFirstSight = false;
if (refreshTask != null) { if (refreshTask != null) {
refreshTask.cancel(true); refreshTask.cancel(true);
refreshTask = null; refreshTask = null;
@ -167,12 +169,6 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
loadingTask.cancel(true); loadingTask.cancel(true);
loadingTask = null; loadingTask = null;
} }
}
@Override
public void onDestroy() {
cancelAsyncTasks();
isFirstSight = false;
super.onDestroy(); super.onDestroy();
} }
} }

View file

@ -95,16 +95,6 @@ public class GradeHeaderItem
? View.INVISIBLE : View.VISIBLE); ? View.INVISIBLE : View.VISIBLE);
} }
@Override
public void onClick(View view) {
super.onClick(view);
if (subjectName.getLineCount() == 1) {
subjectName.setMaxLines(3);
} else {
subjectName.setMaxLines(1);
}
}
private boolean isSubItemsReadAndSaveAlertView(List<GradesSubItem> subItems) { private boolean isSubItemsReadAndSaveAlertView(List<GradesSubItem> subItems) {
boolean isRead = true; boolean isRead = true;

View file

@ -1,70 +0,0 @@
package io.github.wulkanowy.ui.main.settings;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.preference.PreferenceFragmentCompat;
import io.github.wulkanowy.R;
import io.github.wulkanowy.services.SyncJob;
public class SettingsFragment extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String SHARED_KEY_START_TAB = "startup_tab";
public static final String SHARED_KEY_SERVICES_ENABLE = "services_enable";
public static final String SHARED_KEY_NOTIFY_ENABLE = "notify_enable";
public static final String SHARED_KEY_SERVICES_INTERVAL = "services_interval";
public static final String SHARED_KEY_SERVICES_MOBILE_DISABLED = "services_disable_mobile";
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(SHARED_KEY_SERVICES_ENABLE) || key.equals(SHARED_KEY_SERVICES_INTERVAL)
|| key.equals(SHARED_KEY_SERVICES_MOBILE_DISABLED)) {
launchServices(sharedPreferences.getBoolean(SHARED_KEY_SERVICES_ENABLE, true),
sharedPreferences);
}
}
private void launchServices(boolean start, SharedPreferences sharedPref) {
if (start) {
int newInterval = Integer.parseInt(sharedPref.getString(SHARED_KEY_SERVICES_INTERVAL, "60"));
boolean useOnlyWifi = sharedPref.getBoolean(SHARED_KEY_SERVICES_MOBILE_DISABLED, false);
SyncJob.stop(getContext());
SyncJob.start(getContext(), newInterval, useOnlyWifi);
} else {
SyncJob.stop(getContext());
}
}
@Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
if (menuVisible) {
getActivity().setTitle(R.string.settings_text);
}
}
@Override
public void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
}
}

View file

@ -17,16 +17,20 @@ public interface TimetableContract {
void setAdapterWithTabLayout(); void setAdapterWithTabLayout();
void setChildFragmentSelected(int position, boolean selected);
boolean isMenuVisible(); boolean isMenuVisible();
} }
@PerActivity @PerActivity
interface Presenter extends BaseContract.Presenter<View> { interface Presenter extends BaseContract.Presenter<View> {
void onFragmentActivated(boolean isVisible); void onFragmentVisible(boolean isVisible);
void onTabSelected(int position);
void onTabUnselected(int position);
void onStart(View view, OnFragmentIsReadyListener listener); void onStart(View view, OnFragmentIsReadyListener listener);
void setRestoredPosition(int position);
} }
} }

View file

@ -20,9 +20,7 @@ import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.TabsData; import io.github.wulkanowy.ui.main.TabsData;
public class TimetableFragment extends BaseFragment implements TimetableContract.View { public class TimetableFragment extends BaseFragment implements TimetableContract.View, TabLayout.OnTabSelectedListener {
private static final String CURRENT_ITEM_KEY = "CurrentItem";
@BindView(R.id.timetable_fragment_viewpager) @BindView(R.id.timetable_fragment_viewpager)
ViewPager viewPager; ViewPager viewPager;
@ -46,10 +44,6 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
component.inject(this); component.inject(this);
setButterKnife(ButterKnife.bind(this, view)); setButterKnife(ButterKnife.bind(this, view));
presenter.onStart(this, (OnFragmentIsReadyListener) getActivity()); presenter.onStart(this, (OnFragmentIsReadyListener) getActivity());
if (savedInstanceState != null) {
presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY));
}
} }
return view; return view;
} }
@ -58,10 +52,25 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
public void setMenuVisibility(boolean menuVisible) { public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible); super.setMenuVisibility(menuVisible);
if (presenter != null) { if (presenter != null) {
presenter.onFragmentActivated(menuVisible); presenter.onFragmentVisible(menuVisible);
} }
} }
@Override
public void onTabSelected(TabLayout.Tab tab) {
presenter.onTabSelected(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
presenter.onTabUnselected(tab.getPosition());
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
//do nothing
}
@Override @Override
public void setTabDataToAdapter(TabsData tabsData) { public void setTabDataToAdapter(TabsData tabsData) {
pagerAdapter.setTabsData(tabsData); pagerAdapter.setTabsData(tabsData);
@ -70,7 +79,14 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
@Override @Override
public void setAdapterWithTabLayout() { public void setAdapterWithTabLayout() {
viewPager.setAdapter(pagerAdapter); viewPager.setAdapter(pagerAdapter);
tabLayout.setupWithViewPager(viewPager); tabLayout.setupWithViewPager(viewPager);
tabLayout.addOnTabSelectedListener(this);
}
@Override
public void setChildFragmentSelected(int position, boolean selected) {
((TimetableTabFragment) pagerAdapter.getItem(position)).setSelected(selected);
} }
@Override @Override
@ -80,7 +96,7 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
@Override @Override
public void setActivityTitle() { public void setActivityTitle() {
setTitle(getString(R.string.timetable_text)); setTitle(getString(R.string.lessonplan_text));
} }
@Override @Override
@ -91,12 +107,6 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
} }
} }
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem());
super.onSaveInstanceState(outState);
}
@Override @Override
public void onDestroyView() { public void onDestroyView() {
presenter.onDestroy(); presenter.onDestroy();

View file

@ -19,7 +19,6 @@ import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem; import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem;
import eu.davidea.flexibleadapter.items.IFlexible;
import eu.davidea.viewholders.ExpandableViewHolder; import eu.davidea.viewholders.ExpandableViewHolder;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.Day; import io.github.wulkanowy.data.db.dao.entities.Day;
@ -59,13 +58,12 @@ public class TimetableHeaderItem
} }
@Override @Override
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) { public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
return new HeaderViewHolder(view, adapter); return new HeaderViewHolder(view, adapter);
} }
@Override @Override
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, HeaderViewHolder holder, public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List payloads) {
int position, List<Object> payloads) {
holder.onBind(day, getSubItems()); holder.onBind(day, getSubItems());
} }

View file

@ -24,7 +24,7 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
private OnFragmentIsReadyListener listener; private OnFragmentIsReadyListener listener;
private int positionToScroll = 0; private int positionToScroll;
private boolean isFirstSight = false; private boolean isFirstSight = false;
@ -45,10 +45,7 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
if (dates.isEmpty()) { if (dates.isEmpty()) {
dates = TimeUtils.getMondaysFromCurrentSchoolYear(); dates = TimeUtils.getMondaysFromCurrentSchoolYear();
} }
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
if (positionToScroll == 0) {
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
}
if (!isFirstSight) { if (!isFirstSight) {
isFirstSight = true; isFirstSight = true;
@ -60,12 +57,22 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
} }
@Override @Override
public void onFragmentActivated(boolean isVisible) { public void onFragmentVisible(boolean isVisible) {
if (isVisible) { if (isVisible) {
getView().setActivityTitle(); getView().setActivityTitle();
} }
} }
@Override
public void onTabSelected(int position) {
getView().setChildFragmentSelected(position, true);
}
@Override
public void onTabUnselected(int position) {
getView().setChildFragmentSelected(position, false);
}
@Override @Override
public void onDoInBackgroundLoading() throws Exception { public void onDoInBackgroundLoading() throws Exception {
for (String date : dates) { for (String date : dates) {
@ -77,6 +84,7 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
@Override @Override
public void onCanceledLoadingAsync() { public void onCanceledLoadingAsync() {
//do nothing //do nothing
} }
@Override @Override
@ -89,11 +97,6 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
} }
} }
@Override
public void setRestoredPosition(int position) {
this.positionToScroll = position;
}
@Override @Override
public void onDestroy() { public void onDestroy() {
isFirstSight = false; isFirstSight = false;

View file

@ -17,7 +17,6 @@ import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractSectionableItem; import eu.davidea.flexibleadapter.items.AbstractSectionableItem;
import eu.davidea.flexibleadapter.items.IFlexible;
import eu.davidea.viewholders.FlexibleViewHolder; import eu.davidea.viewholders.FlexibleViewHolder;
import io.github.wulkanowy.R; import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
@ -28,7 +27,7 @@ public class TimetableSubItem
private TimetableLesson lesson; private TimetableLesson lesson;
TimetableSubItem(TimetableHeaderItem header, TimetableLesson lesson) { public TimetableSubItem(TimetableHeaderItem header, TimetableLesson lesson) {
super(header); super(header);
this.lesson = lesson; this.lesson = lesson;
} }
@ -63,13 +62,12 @@ public class TimetableSubItem
} }
@Override @Override
public SubItemViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) { public SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
return new SubItemViewHolder(view, adapter); return new SubItemViewHolder(view, adapter);
} }
@Override @Override
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, SubItemViewHolder holder, public void bindViewHolder(FlexibleAdapter adapter, SubItemViewHolder holder, int position, List payloads) {
int position, List<Object> payloads) {
holder.onBind(lesson); holder.onBind(lesson);
} }

View file

@ -23,10 +23,12 @@ public interface TimetableTabContract {
interface Presenter extends BaseContract.Presenter<View> { interface Presenter extends BaseContract.Presenter<View> {
void onFragmentActivated(boolean isSelected); void onFragmentSelected(boolean isSelected);
void setArgumentDate(String date); void setArgumentDate(String date);
void onStart(View view, boolean isPrimary);
void onRefresh(); void onRefresh();
} }
} }

View file

@ -28,6 +28,12 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
private static final String ARGUMENT_KEY = "date"; private static final String ARGUMENT_KEY = "date";
private static final String SAVED_KEY = "isSelected";
private boolean isPrimary = false;
private boolean isSelected = false;
@BindView(R.id.timetable_tab_fragment_recycler) @BindView(R.id.timetable_tab_fragment_recycler)
RecyclerView recyclerView; RecyclerView recyclerView;
@ -49,8 +55,6 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
@Inject @Inject
FlexibleAdapter<TimetableHeaderItem> adapter; FlexibleAdapter<TimetableHeaderItem> adapter;
private boolean isFragmentVisible = false;
public static TimetableTabFragment newInstance(String date) { public static TimetableTabFragment newInstance(String date) {
TimetableTabFragment fragmentTab = new TimetableTabFragment(); TimetableTabFragment fragmentTab = new TimetableTabFragment();
@ -61,6 +65,14 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
return fragmentTab; return fragmentTab;
} }
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
isSelected = savedInstanceState.getBoolean(SAVED_KEY, isSelected);
}
}
@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) {
@ -74,8 +86,8 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
if (getArguments() != null) { if (getArguments() != null) {
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY)); presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
} }
presenter.onStart(this);
presenter.onFragmentActivated(isFragmentVisible); presenter.onStart(this, isPrimary);
} }
return view; return view;
} }
@ -102,9 +114,10 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
@Override @Override
public void setMenuVisibility(boolean menuVisible) { public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible); super.setMenuVisibility(menuVisible);
isFragmentVisible = menuVisible; if (presenter != null && getView() != null) {
if (presenter != null) { presenter.onFragmentSelected(isSelected);
presenter.onFragmentActivated(menuVisible); } else if (isSelected) {
isPrimary = true;
} }
} }
@ -138,6 +151,10 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
} }
public void setSelected(boolean selected) {
isSelected = selected;
}
@Override @Override
public void onError(String message) { public void onError(String message) {
if (getActivity() != null) { if (getActivity() != null) {
@ -146,8 +163,15 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
} }
} }
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(SAVED_KEY, isSelected);
super.onSaveInstanceState(outState);
}
@Override @Override
public void onDestroyView() { public void onDestroyView() {
isPrimary = false;
presenter.onDestroy(); presenter.onDestroy();
super.onDestroyView(); super.onDestroyView();
} }

View file

@ -36,22 +36,21 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
} }
@Override @Override
public void onStart(TimetableTabContract.View view) { public void onStart(TimetableTabContract.View view, boolean isPrimary) {
super.onStart(view); super.onStart(view);
getView().showProgressBar(true); getView().showProgressBar(true);
getView().showNoItem(false); getView().showNoItem(false);
onFragmentSelected(isPrimary);
} }
@Override @Override
public void onFragmentActivated(boolean isSelected) { public void onFragmentSelected(boolean isSelected) {
if (!isFirstSight && isSelected && isViewAttached()) { if (!isFirstSight && isSelected) {
isFirstSight = true; isFirstSight = true;
loadingTask = new AbstractTask(); loadingTask = new AbstractTask();
loadingTask.setOnFirstLoadingListener(this); loadingTask.setOnFirstLoadingListener(this);
loadingTask.execute(); loadingTask.execute();
} else if (!isSelected) {
cancelAsyncTasks();
} }
} }
@ -162,7 +161,10 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
getRepository().syncTimetable(date); getRepository().syncTimetable(date);
} }
private void cancelAsyncTasks() { @Override
public void onDestroy() {
isFirstSight = false;
if (refreshTask != null) { if (refreshTask != null) {
refreshTask.cancel(true); refreshTask.cancel(true);
refreshTask = null; refreshTask = null;
@ -171,12 +173,6 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
loadingTask.cancel(true); loadingTask.cancel(true);
loadingTask = null; loadingTask = null;
} }
}
@Override
public void onDestroy() {
isFirstSight = false;
cancelAsyncTasks();
super.onDestroy(); super.onDestroy();
} }
} }

View file

@ -5,7 +5,6 @@ import android.os.Bundle;
import javax.inject.Inject; import javax.inject.Inject;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import io.github.wulkanowy.services.NotificationService;
import io.github.wulkanowy.services.SyncJob; import io.github.wulkanowy.services.SyncJob;
import io.github.wulkanowy.ui.base.BaseActivity; import io.github.wulkanowy.ui.base.BaseActivity;
import io.github.wulkanowy.ui.login.LoginActivity; import io.github.wulkanowy.ui.login.LoginActivity;
@ -45,12 +44,7 @@ public class SplashActivity extends BaseActivity implements SplashContract.View
} }
@Override @Override
public void startSyncService(int interval, boolean useOnlyWifi) { public void startSyncService() {
SyncJob.start(getApplicationContext(), interval, useOnlyWifi); SyncJob.start(getApplicationContext());
}
@Override
public void cancelNotifications() {
new NotificationService(getApplicationContext()).cancelAll();
} }
} }

View file

@ -12,9 +12,7 @@ public interface SplashContract {
void openMainActivity(); void openMainActivity();
void startSyncService(int interval, boolean useOnlyWifi); void startSyncService();
void cancelNotifications();
} }
@PerActivity @PerActivity

View file

@ -18,12 +18,7 @@ public class SplashPresenter extends BasePresenter<SplashContract.View>
@Override @Override
public void onStart(@NonNull SplashContract.View activity) { public void onStart(@NonNull SplashContract.View activity) {
super.onStart(activity); super.onStart(activity);
getView().cancelNotifications(); getView().startSyncService();
if (getRepository().isServicesEnable()) {
getView().startSyncService(getRepository().getServicesInterval(),
getRepository().isMobileDisable());
}
if (getRepository().getCurrentUserId() == 0) { if (getRepository().getCurrentUserId() == 0) {
getView().openLoginActivity(); getView().openLoginActivity();

View file

@ -1,8 +1,8 @@
package io.github.wulkanowy.utils; package io.github.wulkanowy.utils;
import org.threeten.bp.DayOfWeek; import org.joda.time.DateTime;
import org.threeten.bp.LocalDate; import org.joda.time.DateTimeConstants;
import org.threeten.bp.format.DateTimeFormatter; import org.joda.time.LocalDate;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -13,14 +13,14 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import static io.github.wulkanowy.utils.AppConstant.DATE_PATTERN;
public final class TimeUtils { public final class TimeUtils {
private static final long TICKS_AT_EPOCH = 621355968000000000L; private static final long TICKS_AT_EPOCH = 621355968000000000L;
private static final long TICKS_PER_MILLISECOND = 10000; private static final long TICKS_PER_MILLISECOND = 10000;
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(AppConstant.DATE_PATTERN);
private TimeUtils() { private TimeUtils() {
throw new IllegalStateException("Utility class"); throw new IllegalStateException("Utility class");
} }
@ -33,7 +33,7 @@ public final class TimeUtils {
} }
public static long getNetTicks(String dateString) throws ParseException { public static long getNetTicks(String dateString) throws ParseException {
return getNetTicks(dateString, AppConstant.DATE_PATTERN); return getNetTicks(dateString, DATE_PATTERN);
} }
public static long getNetTicks(String dateString, String dateFormat) throws ParseException { public static long getNetTicks(String dateString, String dateFormat) throws ParseException {
@ -49,12 +49,12 @@ public final class TimeUtils {
} }
public static List<String> getMondaysFromCurrentSchoolYear() { public static List<String> getMondaysFromCurrentSchoolYear() {
LocalDate startDate = LocalDate.of(getCurrentSchoolYear(), 9, 1); LocalDate startDate = new LocalDate(getCurrentSchoolYear(), 9, 1);
LocalDate endDate = LocalDate.of(getCurrentSchoolYear() + 1, 8, 31); LocalDate endDate = new LocalDate(getCurrentSchoolYear() + 1, 8, 31);
List<String> dateList = new ArrayList<>(); List<String> dateList = new ArrayList<>();
LocalDate thisMonday = startDate.with(DayOfWeek.MONDAY); LocalDate thisMonday = startDate.withDayOfWeek(DateTimeConstants.MONDAY);
if (startDate.isAfter(thisMonday)) { if (startDate.isAfter(thisMonday)) {
startDate = thisMonday.plusWeeks(1); startDate = thisMonday.plusWeeks(1);
@ -63,27 +63,27 @@ public final class TimeUtils {
} }
while (startDate.isBefore(endDate)) { while (startDate.isBefore(endDate)) {
dateList.add(startDate.format(formatter)); dateList.add(startDate.toString(DATE_PATTERN));
startDate = startDate.plusWeeks(1); startDate = startDate.plusWeeks(1);
} }
return dateList; return dateList;
} }
public static int getCurrentSchoolYear() { public static int getCurrentSchoolYear() {
LocalDate localDate = LocalDate.now(); DateTime dateTime = new DateTime();
return localDate.getMonthValue() <= 8 ? localDate.getYear() - 1 : localDate.getYear(); return dateTime.getMonthOfYear() <= 8 ? dateTime.getYear() - 1 : dateTime.getYear();
} }
public static String getDateOfCurrentMonday(boolean normalize) { public static String getDateOfCurrentMonday(boolean normalize) {
LocalDate currentDate = LocalDate.now(); DateTime currentDate = new DateTime();
if (currentDate.getDayOfWeek() == DayOfWeek.SATURDAY && normalize) { if (currentDate.getDayOfWeek() == DateTimeConstants.SATURDAY && normalize) {
currentDate = currentDate.plusDays(2); currentDate = currentDate.plusDays(2);
} else if (currentDate.getDayOfWeek() == DayOfWeek.SUNDAY && normalize) { } else if (currentDate.getDayOfWeek() == DateTimeConstants.SUNDAY && normalize) {
currentDate = currentDate.plusDays(1); currentDate = currentDate.plusDays(1);
} else { } else {
currentDate = currentDate.with(DayOfWeek.MONDAY); currentDate = currentDate.withDayOfWeek(DateTimeConstants.MONDAY);
} }
return currentDate.format(formatter); return currentDate.toString(DATE_PATTERN);
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 624 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="services_interval_entries">
<item>10 minut</item>
<item>30 minut</item>
<item>1 godzinę</item>
<item>2 godziny</item>
<item>6 godzin</item>
<item>12 godzin</item>
<item>24 godzin</item>
</string-array>
</resources>

View file

@ -33,12 +33,13 @@
<string name="dashboard_text">Dashboard</string> <string name="dashboard_text">Dashboard</string>
<string name="grades_text">Oceny</string> <string name="grades_text">Oceny</string>
<string name="attendance_text">Frekwencja</string> <string name="attendance_text">Frekwencja</string>
<string name="timetable_text">Plan lekcji</string> <string name="lessonplan_text">Plan lekcji</string>
<string name="settings_text">Ustawienia</string> <string name="settings_text">Ustawienia</string>
<string name="activity_under_construction">Ta część aplikacji jest w budowie</string> <string name="activity_under_construction">Ta część aplikacji jest w budowie</string>
<string name="fragment_no_grades">Brak ocen</string> <string name="fragment_no_grades">Brak ocen</string>
<string name="noInternet_text">Brak połączenia z internetem</string> <string name="noInternet_text">Brak połączenia z internetem</string>
<string name="error_host_offline">Przerwa techniczna. Spróbuj ponownie później</string>
<string name="encrypt_failed_text">Szyfrowanie nie powiodło się. Automatyczne logowanie zostało wyłączone</string> <string name="encrypt_failed_text">Szyfrowanie nie powiodło się. Automatyczne logowanie zostało wyłączone</string>
<string name="version_text">Wersja %1$s</string> <string name="version_text">Wersja %1$s</string>
<string name="refresh_error_text">"Podczas odświeżania zawartości wystąpił błąd. "</string> <string name="refresh_error_text">"Podczas odświeżania zawartości wystąpił błąd. "</string>
@ -113,15 +114,4 @@
<item quantity="few">%1$d nieobecności</item> <item quantity="few">%1$d nieobecności</item>
<item quantity="many">%1$d nieobecności</item> <item quantity="many">%1$d nieobecności</item>
</plurals> </plurals>
<string name="pref_view">Widok</string>
<string name="pref_tab_list">Domyślny widok</string>
<string name="pref_notify">Powiadomienia</string>
<string name="pref_notify_switch">Pokazuj powiadomienia</string>
<string name="pref_services">Usługi</string>
<string name="pref_services_switch">Włącz odświeżanie danych w tle</string>
<string name="pref_services_interval">Interwał między odświeżaniem danych</string>
<string name="pref_services_mobile_data">Synchronizacja tylko przez WiFi</string>
<string name="notify_grade_channel">Nowe oceny</string>
</resources> </resources>

View file

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="startup_tab_entries" translatable="false">
<item>@string/grades_text</item>
<item>@string/attendance_text</item>
<item>@string/dashboard_text</item>
<item>@string/timetable_text</item>
</string-array>
<string-array name="startup_tab_value" translatable="false">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
<string-array name="services_interval_entries">
<item>10 minutes</item>
<item>30 minutes</item>
<item>1 hour</item>
<item>2 hours</item>
<item>6 hours</item>
<item>12 hours</item>
<item>24 hours</item>
</string-array>
<string-array name="services_interval_value" translatable="false">
<item>10</item>
<item>30</item>
<item>60</item>
<item>120</item>
<item>360</item>
<item>720</item>
<item>1440</item>
</string-array>
</resources>

View file

@ -33,7 +33,7 @@
<string name="dashboard_text">Dashboard</string> <string name="dashboard_text">Dashboard</string>
<string name="grades_text">Grades</string> <string name="grades_text">Grades</string>
<string name="attendance_text">Attendance</string> <string name="attendance_text">Attendance</string>
<string name="timetable_text">Timetable</string> <string name="lessonplan_text">Timetable</string>
<string name="settings_text">Settings</string> <string name="settings_text">Settings</string>
<string name="activity_under_construction">This section of app is under construction.</string> <string name="activity_under_construction">This section of app is under construction.</string>
<string name="fragment_no_grades">No grades</string> <string name="fragment_no_grades">No grades</string>
@ -73,6 +73,7 @@
<string name="info_average_grades">Average: %1$.2f</string> <string name="info_average_grades">Average: %1$.2f</string>
<string name="info_no_average">No average</string> <string name="info_no_average">No average</string>
<string name="info_free_week">No lesson in this week</string> <string name="info_free_week">No lesson in this week</string>
<string name="error_host_offline">Technical break</string>
<string name="timetable_subitem_room">Room %s</string> <string name="timetable_subitem_room">Room %s</string>
@ -109,15 +110,4 @@
<item quantity="many">%1$d absences</item> <item quantity="many">%1$d absences</item>
<item quantity="other">%1$d absences</item> <item quantity="other">%1$d absences</item>
</plurals> </plurals>
<string name="pref_tab_list">Default view after startup</string>
<string name="pref_view">View</string>
<string name="pref_notify">Notifications</string>
<string name="pref_notify_switch">Show the notifications</string>
<string name="pref_services">Services</string>
<string name="pref_services_switch">Enable background data refreshing</string>
<string name="pref_services_interval">Interval between data refreshing</string>
<string name="pref_services_mobile_data">Synchronization via WiFi only</string>
<string name="notify_grade_channel">New grades</string>
</resources> </resources>

View file

@ -13,7 +13,6 @@
<item name="titleTextColor">@android:color/primary_text_dark</item> <item name="titleTextColor">@android:color/primary_text_dark</item>
<item name="subtitleTextColor">@android:color/primary_text_dark</item> <item name="subtitleTextColor">@android:color/primary_text_dark</item>
<item name="android:colorBackground">@android:color/white</item> <item name="android:colorBackground">@android:color/white</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
</style> </style>
<style name="WulkanowyTheme.SplashTheme" parent="WulkanowyTheme"> <style name="WulkanowyTheme.SplashTheme" parent="WulkanowyTheme">

View file

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/pref_view">
<ListPreference
android:defaultValue="2"
android:entries="@array/startup_tab_entries"
android:entryValues="@array/startup_tab_value"
android:key="startup_tab"
android:summary="%s"
android:title="@string/pref_tab_list" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_services">
<SwitchPreference
android:defaultValue="true"
android:key="services_enable"
android:title="@string/pref_services_switch" />
<ListPreference
android:defaultValue="60"
android:dependency="services_enable"
android:entries="@array/services_interval_entries"
android:entryValues="@array/services_interval_value"
android:key="services_interval"
android:summary="%s"
android:title="@string/pref_services_interval" />
<SwitchPreference
android:defaultValue="false"
android:key="services_disable_mobile"
android:title="@string/pref_services_mobile_data"
android:dependency="services_enable" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_notify">
<SwitchPreference
android:defaultValue="true"
android:dependency="services_enable"
android:key="notify_enable"
android:title="@string/pref_notify_switch" />
</PreferenceCategory>
</PreferenceScreen>

View file

@ -6,7 +6,10 @@ buildscript {
maven { url "https://plugins.gradle.org/m2/" } maven { url "https://plugins.gradle.org/m2/" }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.1.0' classpath 'com.android.tools.build:gradle:3.0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6.1" classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6.1"
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
@ -26,31 +29,6 @@ subprojects {
ext { ext {
GROUP_ID = "io.github.wulkanowy" GROUP_ID = "io.github.wulkanowy"
supportVersion = "26.1.0"
firebaseJob = "0.8.5"
apacheLang = "3.7"
apacheCollections = "4.1"
flexibleAdapter = "5.0.2"
flexibleUi = "1.0.0-b2"
greenDao = "3.2.2"
greenDaoHelper = "v2.0.2"
butterknife = "8.8.1"
threeTenABP = "1.0.5"
dagger2 = "2.15"
ahbottom = "2.1.0"
jsoup = "1.10.3"
gson = "2.8.2"
debugDb = "1.0.3"
sqlcipher = "3.5.9"
junit = "4.12"
mockito = "2.16.0"
testRunner = "1.0.1"
crashlyticsSdk = "2.9.1"
crashlyticsAnswers = "1.4.1"
} }
allprojects { allprojects {

View file

@ -1,6 +1,6 @@
#Tue Mar 27 02:10:44 CEST 2018 #Thu Oct 27 17:19:04 CEST 2017
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip

View file

@ -1,11 +1,7 @@
apply plugin: "jacoco" apply plugin: "jacoco"
jacoco { jacoco {
toolVersion "0.8.1" toolVersion "0.7.4.201502262128"
}
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
} }
// run ./gradlew clean createDebugCoverageReport jacocoTestReport // run ./gradlew clean createDebugCoverageReport jacocoTestReport