diff --git a/.bettercodehub.yml b/.bettercodehub.yml index 349f7675a..cce850d71 100644 --- a/.bettercodehub.yml +++ b/.bettercodehub.yml @@ -1,3 +1,5 @@ -component_depth: 8 +exclude: +- /app/src/main/java/io/github/wulkanowy/data/db/dao/entities/.* +component_depth: 1 languages: -- kotlin +- java diff --git a/.circleci/config.yml b/.circleci/config.yml index d4e59be19..841ff9e09 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ references: container_config: &container_config docker: - - image: circleci/android:api-28-alpha + - image: circleci/android:api-27-alpha working_directory: *workspace_root environment: environment: @@ -25,8 +25,6 @@ jobs: build: <<: *container_config steps: - - run: | - curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | bash - checkout - restore_cache: <<: *general_cache_key @@ -35,10 +33,10 @@ jobs: command: ./gradlew dependencies --no-daemon --stacktrace --console=plain -PdisablePreDex || true - run: name: Initial build - command: ./gradlew build -x test -x lint -x fabricGenerateResourcesRelease -x packageRelease --no-daemon --stacktrace --console=plain -PdisablePreDex - - run: - name: Run FOSSA - command: fossa --no-ansi || true + command: ./gradlew build assembleDebug -x test -x lint -x fabricGenerateResourcesRelease --no-daemon --stacktrace --console=plain -PdisablePreDex + - store_artifacts: + path: ./app/build/outputs/apk/ + destination: apks/ - persist_to_workspace: root: *workspace_root paths: @@ -80,8 +78,11 @@ jobs: name: Upload unit code coverage to codecov command: bash <(curl -s https://codecov.io/bash) -F app - store_artifacts: - path: ./app/build/reports/ - destination: reports/ + path: ./app/build/reports/tests/ + destination: tests_reports/ + - store_artifacts: + path: ./app/build/reports/jacoco/jacocoTestDebugUnitTestReport/ + destination: coverage_reports/ - store_test_results: path: ./app/build/test-results - persist_to_workspace: @@ -89,20 +90,42 @@ jobs: paths: - "./app/build/jacoco" + api-test: + <<: *container_config + steps: + - *attach_workspace + - restore_cache: + <<: *general_cache_key + - run: + name: Run api tests + command: ./gradlew :api:test :api:jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex + - run: + name: Upload code coverage to codecov + command: bash <(curl -s https://codecov.io/bash) -F api + - store_artifacts: + path: ./api/build/reports/tests/ + destination: tests_reports/ + - store_artifacts: + path: ./api/build/reports/jacoco/test/ + destination: coverage_reports/ + - store_test_results: + path: ./api/build/test-results + - persist_to_workspace: + root: *workspace_root + paths: + - "./api/build/jacoco" + instrumented: <<: *container_config steps: - *attach_workspace - run: name: Setup emulator - command: sdkmanager "system-images;android-19;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-19;default;armeabi-v7a" + command: sdkmanager "system-images;android-19;google_apis;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-19;google_apis;armeabi-v7a" - run: name: Launch emulator command: export LD_LIBRARY_PATH=${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/qt/lib && emulator64-arm -avd test -noaudio -no-boot-anim -no-window -accel on background: true - - run: - name: Change circle-android script file permissions - command: sudo chmod +rx /bin/circle-android - run: name: Wait emulator command: | @@ -111,13 +134,15 @@ jobs: # unlock the emulator screen sleep 30 adb shell input keyevent 82 + - run: + name: Clean project + command: ./gradlew clean --no-daemon --stacktrace --console=plain -PdisablePreDex - run: name: Run instrumented tests - command: ./gradlew clean createDebugCoverageReport jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex + command: ./gradlew createDebugCoverageReport --no-daemon --stacktrace --console=plain -PdisablePreDex - run: name: Collect logs from emulator command: adb logcat -d > ./app/build/reports/logcat_emulator.txt - when: always - run: name: Upload code covarage to codecov command: bash <(curl -s https://codecov.io/bash) -F instrumented @@ -142,66 +167,32 @@ jobs: command: ./gradlew jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex - run: name: Run sonarqube runner - command: if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else ./gradlew -x test -x lint sonarqube -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=$CIRCLE_BRANCH --no-daemon --stacktrace --console=plain -PdisablePreDex; fi - command: "[[ -v CIRCLE_PR_NUMBER ]] && ./gradlew -x test -x lint sonarqube -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=$CIRCLE_BRANCH --no-daemon --stacktrace --console=plain -PdisablePreDex || true" - - deploy: - <<: *container_config - steps: - - *attach_workspace - - restore_cache: - <<: *general_cache_key - - run: - name: Decrypt keys - command: | - gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg - openssl aes-256-cbc -d -in ./app/key-encrypted.p12 -k $ENCRYPT_KEY >> ./app/key.p12 - openssl aes-256-cbc -d -in ./app/upload-key-encrypted.jks -k $ENCRYPT_KEY >> ./app/upload-key.jks - - run: - name: Publish release - command: ./gradlew publish --no-daemon --stacktrace --console=plain -PenableCrashlytics -PdisablePreDex + command: ./gradlew -x test -x lint sonarqube -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=$CIRCLE_BRANCH --no-daemon --stacktrace --console=plain -PdisablePreDex workflows: version: 2 - build-test-deploy: + build_check_tests: jobs: - - build: - filters: - tags: - only: /.*/ + - build - lint: - filters: - tags: - only: /.*/ requires: - build - app-test: - filters: - tags: - only: /.*/ + requires: + - build + - api-test: requires: - build - instrumented: - filters: - tags: - only: /.*/ + requires: + - build requires: - build - sonarcube: - filters: - tags: - only: /.*/ requires: - build - lint - app-test + - api-test - instrumented - - deploy: - requires: - - instrumented - filters: - tags: - only: /\d+\.\d+\.\d+/ - branches: - ignore: /.*/ diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 27d57f599..000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,17 +0,0 @@ -## Co powinno się dziać - - -## Co się dzieje - - -## Jak to zrobić kolejny raz: - - 1. - 2. - 3. - -## Informacje o urządzeniu i dzienniku - - - Wersja aplikacji: - - Wersja Androida: - - Adres URL dziennika: diff --git a/.gitignore b/.gitignore index 6bc531b64..3b524b65e 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,6 @@ local.properties .idea/tasks.xml .idea/vcs.xml .idea/workspace.xml -.idea/caches/ *.iml # OS-specific files @@ -43,8 +42,3 @@ local.properties .Trashes ehthumbs.db Thumbs.db -.idea/caches/ -app/key.p12 -app/upload-key.jks -*.log -.idea/assetWizardSettings.xml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0d1bcd5d2..d8fdd0587 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: circleci/android:api-28-alpha +image: circleci/android:api-27-alpha before_script: - export GRADLE_USER_HOME=`pwd`/.gradle @@ -12,7 +12,7 @@ build: script: - ./gradlew --no-daemon --stacktrace dependencies || true - ./gradlew --no-daemon --stacktrace assembleDebug - - mv app/build/outputs/apk/debug/app-debug.apk . + - mv app/build/outputs/apk/app-debug.apk . artifacts: name: "${CI_PROJECT_NAME}_${CI_BUILD_REF_NAME}-${CI_BUILD_ID}" paths: @@ -26,7 +26,7 @@ tests: - .gradle policy: pull script: - - ./gradlew --no-daemon --stacktrace -x fabricGenerateResourcesRelease test + - ./gradlew --no-daemon --stacktrace test artifacts: paths: - app/build/reports/tests @@ -39,7 +39,7 @@ lint: - .gradle policy: pull script: - - ./gradlew --no-daemon --stacktrace -x fabricGenerateResourcesRelease lint + - ./gradlew --no-daemon --stacktrace lint artifacts: paths: - app/build/reports diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml new file mode 100644 index 000000000..f5bd97e62 --- /dev/null +++ b/.idea/codeStyleSettings.xml @@ -0,0 +1,226 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index a8407c848..000000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,172 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 0f7bc519d..000000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 7ac24c777..48777522e 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -8,6 +8,7 @@ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 56c648589..000000000 --- a/.travis.yml +++ /dev/null @@ -1,68 +0,0 @@ -language: android -jdk: oraclejdk8 - -env: - global: - - ANDROID_API_LEVEL=28 - - ANDROID_BUILD_TOOLS_VERSION=28.0.3 - -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - -#branches: -# only: -# - master -# - 0.7.x - -android: - licenses: - - android-sdk-preview-license-.+ - - android-sdk-license-.+ - - google-gdk-license-.+ - components: - - tools - - platform-tools - # The BuildTools version used by your project - - build-tools-$ANDROID_BUILD_TOOLS_VERSION - # The SDK version used to compile your project - - android-$ANDROID_API_LEVEL - # Additional components - - extra-google-google_play_services - - extra-google-m2repository - - extra-android-m2repository - - addon-google_apis-google-$ANDROID_API_LEVEL - # Android emulator - - android-19 - - sys-img-armeabi-v7a-android-19 - -before_script: - # Launch emulator before the execution - - echo no | android create avd --force -n test -t android-19 --abi armeabi-v7a - - emulator -avd test -no-audio -no-window & - - android-wait-for-emulator - - adb shell input keyevent 82 & - - "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash" - -script: - - ./gradlew dependencies --stacktrace --daemon - - fossa --no-ansi || true - - ./gradlew lint -x fabricGenerateResourcesRelease --stacktrace --daemon - - ./gradlew test -x fabricGenerateResourcesRelease --stacktrace --daemon - - ./gradlew createDebugCoverageReport --stacktrace --daemon - - ./gradlew jacocoTestReport --stacktrace --daemon - - if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else - git fetch --unshallow; - ./gradlew sonarqube -x test -x lint -x fabricGenerateResourcesRelease -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} --stacktrace --daemon; - fi - - | - if [ $TRAVIS_TAG ]; then - gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg; - gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg; - gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg; - ./gradlew publish -PenableCrashlytics --stacktrace; - fi - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/README.md b/README.md index 97a4172c4..65cf14af7 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,12 @@ # Wulkanowy [![CircleCI](https://img.shields.io/circleci/project/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://circleci.com/gh/wulkanowy/wulkanowy) -[![Travis](https://img.shields.io/travis/com/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://travis-ci.com/wulkanowy/wulkanowy) [![Bitrise](https://img.shields.io/bitrise/daeff1893f3c8128/master.svg?token=Hjm1ACamk86JDeVVJHOeqQ&style=flat-square)](https://www.bitrise.io/app/daeff1893f3c8128) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![BCH compliance](https://bettercodehub.com/edge/badge/wulkanowy/wulkanowy?branch=master)](https://bettercodehub.com/) -[![Sonarcloud](https://sonarcloud.io/api/project_badges/measure?project=io.github.wulkanowy%3Aapp&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=io.github.wulkanowy%3Aapp) -[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy.svg?type=shield)](https://app.fossa.com/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy?ref=badge_shield) -[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) +[![Known Vulnerabilities](https://snyk.io/test/github/wulkanowy/wulkanowy/badge.svg?targetFile=app%2Fbuild.gradle&style=flat-square)](https://snyk.io/test/github/wulkanowy/wulkanowy?targetFile=app%2Fbuild.gradle) +[![Bintray](https://img.shields.io/bintray/v/wulkanowy/wulkanowy/api.svg?style=flat-square)](https://bintray.com/wulkanowy/wulkanowy/api) -[Pobierz wersję beta z Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy&utm_source=vcs) +[Pobierz wersję rozwojową](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/app-debug-bitrise-signed.apk) -[Pobierz wersję DEV](https://bitrise-redirector.herokuapp.com/v0.1/apps/f841f20d8f8b1dc8/builds/master/artifacts/0) -[(Więcej wersji DEV)](https://wulkanowy.github.io/dev.html) - -Androidowy klient dziennika VULCAN UONET+. - - -## License - -[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy.svg?type=large)](https://app.fossa.com/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy?ref=badge_large) +Wulkanowy to aplikacja na androida polepszająca wygodę używania dziennika UONET+. diff --git a/app/sonarqube.gradle b/android-sonarqube.gradle similarity index 73% rename from app/sonarqube.gradle rename to android-sonarqube.gradle index 52fd2dd8b..b9284eeac 100644 --- a/app/sonarqube.gradle +++ b/android-sonarqube.gradle @@ -4,13 +4,16 @@ sonarqube { //noinspection GroovyAssignabilityCheck properties { - def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-28/android.jar" + def files = fileTree("${rootProject.projectDir}/api/build/libs/").filter { it.isFile() }.files.name + def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-27/android.jar," + + "${project.rootDir}/api/build/libs/" + files[0] - property "sonar.projectName", "io.github.wulkanowy:app" - property "sonar.projectKey", "io.github.wulkanowy:app" + property "sonar.projectName", GROUP_ID + ":app" + property "sonar.projectKey", GROUP_ID + ":app" property "sonar.sources", "src/main/java" - property "sonar.exclusions", "build/**,**/*.png,*.iml, **/*generated*," + property "sonar.exclusions", "build/**,**/*.png,*.iml, **/*generated*," + + "src/**/entities/*.java, src/androidTest/**/entities/*.java" property "sonar.import_unknown_files", true // Defines where the java files are diff --git a/api/build.gradle b/api/build.gradle new file mode 100644 index 000000000..ebaca4c87 --- /dev/null +++ b/api/build.gradle @@ -0,0 +1,117 @@ +apply plugin: 'java-library' +apply plugin: 'org.sonarqube' +apply plugin: 'jacoco' +apply plugin: 'com.jfrog.bintray' +apply plugin: 'com.github.dcendents.android-maven' + +compileJava.options.encoding = "UTF-8" +compileTestJava.options.encoding = "UTF-8" + +ext { + PUBLISH_GROUP_ID = GROUP_ID + PUBLISH_ARTIFACT_ID = 'api' + PUBLISH_VERSION = System.getenv('GIT_TAG') +} + +test { + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } +} + +jacocoTestReport { + reports { + xml.enabled true + } +} + +dependencies { + implementation 'org.jsoup:jsoup:1.10.3' + implementation 'org.apache.commons:commons-lang3:3.7' + implementation 'com.google.code.gson:gson:2.8.2' + + testImplementation 'junit:junit:4.12' + testImplementation 'org.mockito:mockito-core:2.13.0' +} + +version = PUBLISH_VERSION +group = GROUP_ID + +sonarqube { + properties { + property "sonar.projectName", GROUP_ID + ":api" + property "sonar.projectKey", GROUP_ID + ":wulkanowy-api" + } +} + +def siteUrl = 'https://github.com/wulkanowy/wulkanowy' +def gitUrl = 'https://github.com/wulkanowy/wulkanowy.git' + +bintray { + user = System.getenv('BINTRAY_USER') + key = System.getenv('BINTRAY_KEY') + configurations = ['archives'] + pkg { + repo = 'wulkanowy' + name = 'api' + userOrg = 'wulkanowy' + licenses = ['Apache-2.0'] + vcsUrl = gitUrl + labels = ['aar', 'android', 'wulkanowy', 'api'] + publicDownloadNumbers = true + publish = true + + version { + name = PUBLISH_VERSION + vcsTag = PUBLISH_VERSION + released = new Date() + } + } +} + +install { + repositories.mavenInstaller { + pom { + project { + packaging 'aar' + name 'Bintray publish Gradle aar' + url siteUrl + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id 'mklkj' + name 'Mikołaj Pich' + email 'm.pich@outlook.com' + } + } + scm { + connection gitUrl + developerConnection gitUrl + url siteUrl + } + } + } + } +} + +task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar +} diff --git a/api/src/main/java/io/github/wulkanowy/api/Client.java b/api/src/main/java/io/github/wulkanowy/api/Client.java new file mode 100644 index 000000000..16b7982c9 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/Client.java @@ -0,0 +1,180 @@ +package io.github.wulkanowy.api; + +import org.jsoup.Connection; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; + +import java.io.IOException; +import java.util.Date; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import io.github.wulkanowy.api.login.Login; + +public class Client { + + private String protocol = "https"; + + private String host = "vulcan.net.pl"; + + private String email; + + private String password; + + private String symbol = "Default"; + + private Login login; + + private Date lastSuccessRequest = new Date(); + + private Cookies cookies = new Cookies(); + + Client(String email, String password, String symbol) { + this.email = email; + this.password = password; + this.symbol = symbol; + + setFullEndpointInfo(email); + } + + private void setFullEndpointInfo(String info) { + String[] creds = info.split("\\\\"); + + email = info; + + if (creds.length > 2) { + String[] url = creds[0].split("://"); + + protocol = url[0]; + host = url[1]; + email = creds[2]; + } + } + + private void login() throws IOException, VulcanException { + if (isLoggedIn()) { + return; + } + + this.symbol = getLogin().login(email, password, symbol); + } + + private boolean isLoggedIn() { + return getCookies().size() > 0 && + 29 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime()); + + } + + Login getLogin() { + if (null != login) { + return login; + } + + login = new Login(this); + + return login; + } + + public String getSymbol() { + return symbol; + } + + public void setSymbol(String symbol) { + this.symbol = symbol; + } + + private Map getCookies() { + return cookies.getItems(); + } + + String getHost() { + return host; + } + + String getFilledUrl(String url) { + return url + .replace("{schema}", protocol) + .replace("{host}", host.replace(":", "%253A")) + .replace("{symbol}", symbol); + } + + Document getPageByUrl(String url) throws IOException, VulcanException { + login(); + + Connection.Response response = Jsoup.connect(getFilledUrl(url)) + .followRedirects(true) + .cookies(getCookies()) + .execute(); + + this.cookies.addItems(response.cookies()); + + return checkForErrors(response.parse()); + } + + public Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException { + Connection connection = Jsoup.connect(getFilledUrl(url)); + + for (String[] data : params) { + connection.data(data[0], data[1]); + } + + Connection.Response response = connection + .followRedirects(true) + .method(Connection.Method.POST) + .cookies(getCookies()) + .execute(); + + this.cookies.addItems(response.cookies()); + + return checkForErrors(response.parse()); + } + + public String getJsonStringByUrl(String url) throws IOException, VulcanException { + login(); + + Connection.Response response = Jsoup.connect(getFilledUrl(url)) + .followRedirects(true) + .ignoreContentType(true) + .cookies(getCookies()) + .execute(); + + this.cookies.addItems(response.cookies()); + + return response.body(); + } + + public String postJsonStringByUrl(String url, String[][] params) throws IOException, VulcanException { + login(); + + Connection connection = Jsoup.connect(getFilledUrl(url)); + + for (String[] data : params) { + connection.data(data[0], data[1]); + } + + Connection.Response response = connection + .followRedirects(true) + .ignoreContentType(true) + .method(Connection.Method.POST) + .cookies(getCookies()) + .execute(); + + this.cookies.addItems(response.cookies()); + + return response.body(); + } + + Document checkForErrors(Document doc) throws VulcanException { + if ("Przerwa techniczna".equals(doc.select("title").text())) { + throw new VulcanOfflineException(); + } + + if ("Zaloguj się".equals(doc.select(".loginButton").text())) { + throw new NotLoggedInErrorException(); + } + + lastSuccessRequest = new Date(); + + return doc; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/Cookies.java b/api/src/main/java/io/github/wulkanowy/api/Cookies.java new file mode 100644 index 000000000..dfe4c4b54 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/Cookies.java @@ -0,0 +1,17 @@ +package io.github.wulkanowy.api; + +import java.util.HashMap; +import java.util.Map; + +class Cookies { + + private Map jar = new HashMap<>(); + + Map getItems() { + return jar; + } + + void addItems(Map items) { + jar.putAll(items); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/NotLoggedInErrorException.java b/api/src/main/java/io/github/wulkanowy/api/NotLoggedInErrorException.java new file mode 100644 index 000000000..86372266e --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/NotLoggedInErrorException.java @@ -0,0 +1,4 @@ +package io.github.wulkanowy.api; + +public class NotLoggedInErrorException extends VulcanException { +} diff --git a/api/src/main/java/io/github/wulkanowy/api/Semester.java b/api/src/main/java/io/github/wulkanowy/api/Semester.java new file mode 100644 index 000000000..64a07c976 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/Semester.java @@ -0,0 +1,37 @@ +package io.github.wulkanowy.api; + +public class Semester { + + private String number = ""; + + private String id = ""; + + private boolean isCurrent = false; + + public String getNumber() { + return number; + } + + public Semester setNumber(String number) { + this.number = number; + return this; + } + + public String getId() { + return id; + } + + public Semester setId(String id) { + this.id = id; + return this; + } + + public boolean isCurrent() { + return isCurrent; + } + + public Semester setCurrent(boolean current) { + isCurrent = current; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/SnP.java b/api/src/main/java/io/github/wulkanowy/api/SnP.java new file mode 100644 index 000000000..adbffd4b0 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/SnP.java @@ -0,0 +1,24 @@ +package io.github.wulkanowy.api; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; + +import java.io.IOException; +import java.util.List; + +public interface SnP { + + String getId(); + + StudentAndParent storeContextCookies() throws IOException, VulcanException; + + String getRowDataChildValue(Element e, int index); + + Document getSnPPageDocument(String url) throws IOException, VulcanException; + + List getSemesters() throws IOException, VulcanException; + + List getSemesters(Document gradesPage); + + Semester getCurrentSemester(List semesterList); +} diff --git a/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java b/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java new file mode 100644 index 000000000..c014e96cd --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java @@ -0,0 +1,114 @@ +package io.github.wulkanowy.api; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class StudentAndParent implements SnP { + + private static final String START_PAGE_URL = "{schema}://uonetplus.{host}/{symbol}/Start.mvc/Index"; + + private static final String BASE_URL = "{schema}://uonetplus-opiekun.{host}/{symbol}/{ID}/"; + + private static final String GRADES_PAGE_URL = "Oceny/Wszystkie"; + + private Client client; + + private String id; + + StudentAndParent(Client client, String id) { + this.client = client; + this.id = id; + } + + private String getBaseUrl() { + return BASE_URL.replace("{ID}", getId()); + } + + public String getId() { + return id; + } + + public StudentAndParent storeContextCookies() throws IOException, VulcanException { + client.getPageByUrl(getSnpHomePageUrl()); + return this; + } + + String getSnpHomePageUrl() throws IOException, VulcanException { + if (null != getId()) { + return getBaseUrl(); + } + + // get url to uonetplus-opiekun.vulcan.net.pl + Document startPage = client.getPageByUrl(START_PAGE_URL); + Element studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a").first(); + + if (null == studentTileLink) { + throw new NotLoggedInErrorException(); + } + + String snpPageUrl = studentTileLink.attr("href"); + + this.id = getExtractedIdFromUrl(snpPageUrl); + + return snpPageUrl; + } + + String getExtractedIdFromUrl(String snpPageUrl) throws NotLoggedInErrorException { + String[] path = snpPageUrl.split(client.getHost())[1].split("/"); + + if (5 != path.length) { + throw new NotLoggedInErrorException(); + } + + return path[2]; + } + + public String getRowDataChildValue(Element e, int index) { + return e.select(".daneWiersz .wartosc").get(index - 1).text(); + } + + public Document getSnPPageDocument(String url) throws IOException, VulcanException { + return client.getPageByUrl(getBaseUrl() + url); + } + + public List getSemesters() throws IOException, VulcanException { + return getSemesters(getSnPPageDocument(GRADES_PAGE_URL)); + } + + public List getSemesters(Document gradesPage) { + Elements semesterOptions = gradesPage.select("#okresyKlasyfikacyjneDropDownList option"); + + List semesters = new ArrayList<>(); + + for (Element e : semesterOptions) { + Semester semester = new Semester() + .setId(e.text()) + .setNumber(e.attr("value")); + + if ("selected".equals(e.attr("selected"))) { + semester.setCurrent(true); + } + + semesters.add(semester); + } + + return semesters; + } + + public Semester getCurrentSemester(List semesterList) { + Semester current = null; + for (Semester s : semesterList) { + if (s.isCurrent()) { + current = s; + break; + } + } + + return current; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/Vulcan.java b/api/src/main/java/io/github/wulkanowy/api/Vulcan.java new file mode 100644 index 000000000..c16ef0889 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/Vulcan.java @@ -0,0 +1,111 @@ +package io.github.wulkanowy.api; + +import java.io.IOException; + +import io.github.wulkanowy.api.attendance.AttendanceStatistics; +import io.github.wulkanowy.api.attendance.AttendanceTable; +import io.github.wulkanowy.api.exams.ExamsWeek; +import io.github.wulkanowy.api.grades.GradesList; +import io.github.wulkanowy.api.grades.SubjectsList; +import io.github.wulkanowy.api.messages.Messages; +import io.github.wulkanowy.api.notes.AchievementsList; +import io.github.wulkanowy.api.notes.NotesList; +import io.github.wulkanowy.api.school.SchoolInfo; +import io.github.wulkanowy.api.school.TeachersInfo; +import io.github.wulkanowy.api.timetable.Timetable; +import io.github.wulkanowy.api.user.BasicInformation; +import io.github.wulkanowy.api.user.FamilyInformation; + +public class Vulcan { + + private String id; + + private SnP snp; + + private Client client; + + public void setCredentials(String email, String password, String symbol, String id) { + client = new Client(email, password, symbol); + + this.id = id; + } + + public Client getClient() throws NotLoggedInErrorException { + if (null == client) { + throw new NotLoggedInErrorException(); + } + + return client; + } + + public String getSymbol() throws NotLoggedInErrorException { + return getClient().getSymbol(); + + } + + public SnP getStudentAndParent() throws IOException, VulcanException { + if (null != this.snp) { + return this.snp; + } + + this.snp = new StudentAndParent(getClient(), id).storeContextCookies(); + + return this.snp; + } + + public String getId() throws IOException, VulcanException { + return getStudentAndParent().getId(); + } + + public AttendanceTable getAttendanceTable() throws IOException, VulcanException { + return new AttendanceTable(getStudentAndParent()); + } + + public AttendanceStatistics getAttendanceStatistics() throws IOException, VulcanException { + return new AttendanceStatistics(getStudentAndParent()); + } + + public ExamsWeek getExamsList() throws IOException, VulcanException { + return new ExamsWeek(getStudentAndParent()); + } + + public GradesList getGradesList() throws IOException, VulcanException { + return new GradesList(getStudentAndParent()); + } + + public SubjectsList getSubjectsList() throws IOException, VulcanException { + return new SubjectsList(getStudentAndParent()); + } + + public AchievementsList getAchievementsList() throws IOException, VulcanException { + return new AchievementsList(getStudentAndParent()); + } + + public NotesList getNotesList() throws IOException, VulcanException { + return new NotesList(getStudentAndParent()); + } + + public SchoolInfo getSchoolInfo() throws IOException, VulcanException { + return new SchoolInfo(getStudentAndParent()); + } + + public TeachersInfo getTeachersInfo() throws IOException, VulcanException { + return new TeachersInfo(getStudentAndParent()); + } + + public Timetable getTimetable() throws IOException, VulcanException { + return new Timetable(getStudentAndParent()); + } + + public BasicInformation getBasicInformation() throws IOException, VulcanException { + return new BasicInformation(getStudentAndParent()); + } + + public FamilyInformation getFamilyInformation() throws IOException, VulcanException { + return new FamilyInformation(getStudentAndParent()); + } + + public Messages getMessages() throws VulcanException { + return new Messages(getClient()); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/VulcanException.java b/api/src/main/java/io/github/wulkanowy/api/VulcanException.java new file mode 100644 index 000000000..0e7ed2439 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/VulcanException.java @@ -0,0 +1,4 @@ +package io.github.wulkanowy.api; + +public abstract class VulcanException extends Exception { +} diff --git a/api/src/main/java/io/github/wulkanowy/api/VulcanOfflineException.java b/api/src/main/java/io/github/wulkanowy/api/VulcanOfflineException.java new file mode 100644 index 000000000..497fba942 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/VulcanOfflineException.java @@ -0,0 +1,4 @@ +package io.github.wulkanowy.api; + +public class VulcanOfflineException extends VulcanException { +} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceStatistics.java b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceStatistics.java new file mode 100644 index 000000000..29b6a6d99 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceStatistics.java @@ -0,0 +1,89 @@ +package io.github.wulkanowy.api.attendance; + +import org.apache.commons.lang3.math.NumberUtils; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; +import io.github.wulkanowy.api.generic.Month; +import io.github.wulkanowy.api.generic.Subject; + +public class AttendanceStatistics { + + private SnP snp; + + private String attendancePageUrl = "Frekwencja.mvc"; + + public AttendanceStatistics(SnP snp) { + this.snp = snp; + } + + public Types getTypesTable() throws IOException, VulcanException { + return getTypesTable(""); + } + + public Types getTypesTable(String tick) throws IOException, VulcanException { + return getTypesTable(tick, -1); + } + + public List getSubjectList() throws IOException, VulcanException { + Element mainContainer = snp.getSnPPageDocument(attendancePageUrl) + .select(".mainContainer #idPrzedmiot").first(); + + List subjectList = new ArrayList<>(); + + for (Element subject : mainContainer.select("option")) { + subjectList.add(new Subject() + .setId(Integer.parseInt(subject.attr("value"))) + .setName(subject.text()) + ); + } + + return subjectList; + } + + public Types getTypesTable(String tick, Integer subjectId) throws IOException, VulcanException { + Element mainContainer = snp.getSnPPageDocument((attendancePageUrl + + "?data={tick}&idPrzedmiot={subject}") + .replace("{tick}", tick) + .replace("{subject}", subjectId.toString()) + ).select(".mainContainer").first(); + + Element table = mainContainer.select("table:nth-of-type(2)").first(); + + Elements headerCells = table.select("thead th"); + List typeList = new ArrayList<>(); + + Elements typesRows = table.select("tbody tr"); + + // fill types with months + for (Element row : typesRows) { + Elements monthsCells = row.select("td"); + + List monthList = new ArrayList<>(); + + // iterate over month in type, first column is empty, last is `total`; (0, n-1) + for (int i = 1; i < monthsCells.size() - 1; i++) { + monthList.add(new Month() + .setValue(NumberUtils.toInt(monthsCells.get(i).text(), 0)) + .setName(headerCells.get(i).text())); + } + + typeList.add(new Type() + .setTotal(NumberUtils.toInt(monthsCells.last().text(), 0)) + .setName(monthsCells.get(0).text()) + .setMonthList(monthList)); + } + + String total = mainContainer.select("h2").text().split(": ")[1]; + + return new Types() + .setTotal(NumberUtils.toDouble(total.replace("%", "").replace(",", "."))) + .setTypeList(typeList); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java new file mode 100644 index 000000000..c45cbe6a4 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java @@ -0,0 +1,117 @@ +package io.github.wulkanowy.api.attendance; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; +import io.github.wulkanowy.api.generic.Day; +import io.github.wulkanowy.api.generic.Lesson; +import io.github.wulkanowy.api.generic.Week; + +public class AttendanceTable { + + private final static String ATTENDANCE_PAGE_URL = "Frekwencja.mvc?data="; + + private SnP snp; + + public AttendanceTable(SnP snp) { + this.snp = snp; + } + + public Week getWeekTable() throws IOException, ParseException, VulcanException { + return getWeekTable(""); + } + + public Week getWeekTable(String tick) throws IOException, ParseException, VulcanException { + Element table = snp.getSnPPageDocument(ATTENDANCE_PAGE_URL + tick) + + .select(".mainContainer .presentData").first(); + + Elements headerCells = table.select("thead th"); + List days = new ArrayList<>(); + + for (int i = 1; i < headerCells.size(); i++) { + String[] dayHeaderCell = headerCells.get(i).html().split("
"); + + SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT); + Date d = sdf.parse(dayHeaderCell[1].trim()); + sdf.applyPattern("yyyy-MM-dd"); + + Day day = new Day(); + day.setDayName(dayHeaderCell[0]); + day.setDate(sdf.format(d)); + days.add(day); + } + + Elements hoursInDays = table.select("tbody tr"); + + // fill days in week with lessons + for (Element row : hoursInDays) { + Elements hours = row.select("td"); + + // fill hours in day + int size = hours.size(); + for (int i = 1; i < size; i++) { + Lesson lesson = new Lesson(); + lesson.setDate(days.get(i - 1).getDate()); + lesson.setNumber(hours.get(0).text()); + + addLessonDetails(lesson, hours.get(i)); + + days.get(i - 1).setLesson(lesson); + } + } + + return new Week() + .setStartDayDate(days.get(0).getDate()) + .setDays(days); + } + + private void addLessonDetails(Lesson lesson, Element cell) { + lesson.setSubject(cell.select("span").text()); + + if (LessonTypes.CLASS_NOT_EXIST.equals(cell.attr("class"))) { + lesson.setNotExist(true); + lesson.setEmpty(true); + + return; + } + + switch (cell.select("div").attr("class")) { + case LessonTypes.CLASS_PRESENCE: + lesson.setPresence(true); + break; + case LessonTypes.CLASS_ABSENCE_UNEXCUSED: + lesson.setAbsenceUnexcused(true); + break; + case LessonTypes.CLASS_ABSENCE_EXCUSED: + lesson.setAbsenceExcused(true); + break; + case LessonTypes.CLASS_ABSENCE_FOR_SCHOOL_REASONS: + lesson.setAbsenceForSchoolReasons(true); + break; + case LessonTypes.CLASS_UNEXCUSED_LATENESS: + lesson.setUnexcusedLateness(true); + break; + case LessonTypes.CLASS_EXCUSED_LATENESS: + lesson.setExcusedLateness(true); + break; + case LessonTypes.CLASS_EXEMPTION: + lesson.setExemption(true); + break; + + default: + lesson.setEmpty(true); + break; + } + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/LessonTypes.java b/api/src/main/java/io/github/wulkanowy/api/attendance/LessonTypes.java new file mode 100644 index 000000000..a9a1fac4e --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/LessonTypes.java @@ -0,0 +1,24 @@ +package io.github.wulkanowy.api.attendance; + +class LessonTypes { + + static final String CLASS_NOT_EXIST = "x-sp-nieobecny-w-oddziale"; + + static final String CLASS_PRESENCE = "x-obecnosc"; + + static final String CLASS_ABSENCE_UNEXCUSED = "x-nieobecnosc-nieuspr"; + + static final String CLASS_ABSENCE_EXCUSED = "x-nieobecnosc-uspr"; + + static final String CLASS_ABSENCE_FOR_SCHOOL_REASONS = "x-nieobecnosc-przycz-szkol"; + + static final String CLASS_UNEXCUSED_LATENESS = "x-sp-nieusprawiedliwione"; + + static final String CLASS_EXCUSED_LATENESS = "x-sp-spr"; + + static final String CLASS_EXEMPTION = "x-sp-zwolnienie"; + + private LessonTypes() { + throw new IllegalStateException("Utility class"); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/Type.java b/api/src/main/java/io/github/wulkanowy/api/attendance/Type.java new file mode 100644 index 000000000..6deb003e1 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/Type.java @@ -0,0 +1,42 @@ +package io.github.wulkanowy.api.attendance; + +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.generic.Month; + +public class Type { + + private String name = ""; + + private int total = 0; + + private List monthList = new ArrayList<>(); + + public String getName() { + return name; + } + + public Type setName(String name) { + this.name = name; + return this; + } + + public int getTotal() { + return total; + } + + public Type setTotal(int total) { + this.total = total; + return this; + } + + public List getMonthList() { + return monthList; + } + + public Type setMonthList(List monthList) { + this.monthList = monthList; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/Types.java b/api/src/main/java/io/github/wulkanowy/api/attendance/Types.java new file mode 100644 index 000000000..d1b1777de --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/Types.java @@ -0,0 +1,29 @@ +package io.github.wulkanowy.api.attendance; + +import java.util.ArrayList; +import java.util.List; + +public class Types { + + private double total = 0; + + private List typeList = new ArrayList<>(); + + public double getTotal() { + return total; + } + + public Types setTotal(double total) { + this.total = total; + return this; + } + + public List getTypeList() { + return typeList; + } + + public Types setTypeList(List typeList) { + this.typeList = typeList; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/exams/Exam.java b/api/src/main/java/io/github/wulkanowy/api/exams/Exam.java new file mode 100644 index 000000000..466bfe4f7 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/exams/Exam.java @@ -0,0 +1,59 @@ +package io.github.wulkanowy.api.exams; + +public class Exam { + + private String subjectAndGroup = ""; + + private String type = ""; + + private String description = ""; + + private String teacher = ""; + + private String entryDate = ""; + + public String getSubjectAndGroup() { + return subjectAndGroup; + } + + public Exam setSubjectAndGroup(String subjectAndGroup) { + this.subjectAndGroup = subjectAndGroup; + return this; + } + + public String getType() { + return type; + } + + public Exam setType(String type) { + this.type = type; + return this; + } + + public String getDescription() { + return description; + } + + public Exam setDescription(String description) { + this.description = description; + return this; + } + + public String getTeacher() { + return teacher; + } + + public Exam setTeacher(String teacher) { + this.teacher = teacher; + return this; + } + + public String getEntryDate() { + return entryDate; + } + + public Exam setEntryDate(String entryDate) { + this.entryDate = entryDate; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/exams/ExamDay.java b/api/src/main/java/io/github/wulkanowy/api/exams/ExamDay.java new file mode 100644 index 000000000..8127e631e --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/exams/ExamDay.java @@ -0,0 +1,19 @@ +package io.github.wulkanowy.api.exams; + +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.generic.Day; + +public class ExamDay extends Day { + + private List examList = new ArrayList<>(); + + public List getExamList() { + return examList; + } + + public void addExam(Exam exam) { + this.examList.add(exam); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java b/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java new file mode 100644 index 000000000..3dd056d9a --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java @@ -0,0 +1,65 @@ +package io.github.wulkanowy.api.exams; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; +import io.github.wulkanowy.api.generic.Week; + +public class ExamsWeek { + + private static final String EXAMS_PAGE_URL = "Sprawdziany.mvc/Terminarz?rodzajWidoku=2&data="; + + private final SnP snp; + + public ExamsWeek(SnP snp) { + this.snp = snp; + } + + public Week getCurrent() throws IOException, VulcanException { + return getWeek("", true); + } + + public Week getWeek(String tick, final boolean onlyNotEmpty) throws IOException, VulcanException { + Document examsPage = snp.getSnPPageDocument(EXAMS_PAGE_URL + tick); + Elements examsDays = examsPage.select(".mainContainer > div:not(.navigation)"); + + List days = new ArrayList<>(); + + for (Element item : examsDays) { + ExamDay day = new ExamDay(); + Element dayHeading = item.select("h2").first(); + + if (null == dayHeading && onlyNotEmpty) { + continue; + } + + if (null != dayHeading) { + day.setDate(dayHeading.text().split(", ")[1]); + } + + Elements exams = item.select("article"); + for (Element e : exams) { + day.addExam(new Exam() + .setSubjectAndGroup(snp.getRowDataChildValue(e, 1)) + .setType(snp.getRowDataChildValue(e, 2)) + .setDescription(snp.getRowDataChildValue(e, 3)) + .setTeacher(snp.getRowDataChildValue(e, 4).split(", ")[0]) + .setEntryDate(snp.getRowDataChildValue(e, 4).split(", ")[1]) + ); + } + + days.add(day); + } + + return new Week() + .setStartDayDate(examsDays.select("h2").first().text().split(" ")[1]) + .setDays(days); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Day.java b/api/src/main/java/io/github/wulkanowy/api/generic/Day.java new file mode 100644 index 000000000..6bf26c040 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Day.java @@ -0,0 +1,63 @@ +package io.github.wulkanowy.api.generic; + +import java.util.ArrayList; +import java.util.List; + +public class Day { + + private List lessons = new ArrayList<>(); + + protected String date = ""; + + private String dayName = ""; + + private boolean isFreeDay = false; + + private String freeDayName = ""; + + public Lesson getLesson(int index) { + return lessons.get(index); + } + + public List getLessons() { + return lessons; + } + + public Day setLesson(Lesson lesson) { + this.lessons.add(lesson); + return this; + } + + public String getDate() { + return date; + } + + public Day setDate(String date) { + this.date = date; + return this; + } + + public String getDayName() { + return dayName; + } + + public void setDayName(String dayName) { + this.dayName = dayName; + } + + public boolean isFreeDay() { + return isFreeDay; + } + + public void setFreeDay(boolean freeDay) { + isFreeDay = freeDay; + } + + public String getFreeDayName() { + return freeDayName; + } + + public void setFreeDayName(String freeDayName) { + this.freeDayName = freeDayName; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Lesson.java b/api/src/main/java/io/github/wulkanowy/api/generic/Lesson.java new file mode 100644 index 000000000..8dd653277 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Lesson.java @@ -0,0 +1,240 @@ +package io.github.wulkanowy.api.generic; + +public class Lesson { + + private String number = ""; + + private String subject = ""; + + private String teacher = ""; + + private String room = ""; + + private String description = ""; + + private String groupName = ""; + + private String startTime = ""; + + private String endTime = ""; + + private String date = ""; + + private boolean isEmpty = false; + + private boolean isDivisionIntoGroups = false; + + private boolean isPlanning = false; + + private boolean isRealized = false; + + private boolean isMovedOrCanceled = false; + + private boolean isNewMovedInOrChanged = false; + + private boolean isNotExist = false; + + private boolean isPresence = false; + + private boolean isAbsenceUnexcused = false; + + private boolean isAbsenceExcused = false; + + private boolean isUnexcusedLateness = false; + + private boolean isAbsenceForSchoolReasons = false; + + private boolean isExcusedLateness = false; + + private boolean isExemption = false; + + public String getNumber() { + return number; + } + + public void setNumber(String number) { + this.number = number; + } + + public String getSubject() { + return subject; + } + + public Lesson setSubject(String subject) { + this.subject = subject; + return this; + } + + public String getTeacher() { + return teacher; + } + + public Lesson setTeacher(String teacher) { + this.teacher = teacher; + return this; + } + + public String getRoom() { + return room; + } + + public Lesson setRoom(String room) { + this.room = room; + return this; + } + + public String getDescription() { + return description; + } + + public Lesson setDescription(String description) { + this.description = description; + return this; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getDate() { + return date; + } + + public Lesson setDate(String date) { + this.date = date; + return this; + } + + public boolean isEmpty() { + return isEmpty; + } + + public Lesson setEmpty(boolean empty) { + isEmpty = empty; + return this; + } + + public boolean isDivisionIntoGroups() { + return isDivisionIntoGroups; + } + + public void setDivisionIntoGroups(boolean divisionIntoGroups) { + isDivisionIntoGroups = divisionIntoGroups; + } + + public boolean isPlanning() { + return isPlanning; + } + + public void setPlanning(boolean planning) { + isPlanning = planning; + } + + public boolean isRealized() { + return isRealized; + } + + public void setRealized(boolean realized) { + isRealized = realized; + } + + public boolean isMovedOrCanceled() { + return isMovedOrCanceled; + } + + public void setMovedOrCanceled(boolean movedOrCanceled) { + isMovedOrCanceled = movedOrCanceled; + } + + public boolean isNewMovedInOrChanged() { + return isNewMovedInOrChanged; + } + + public void setNewMovedInOrChanged(boolean newMovedInOrChanged) { + isNewMovedInOrChanged = newMovedInOrChanged; + } + + public boolean isNotExist() { + return isNotExist; + } + + public void setNotExist(boolean notExist) { + isNotExist = notExist; + } + + public boolean isPresence() { + return isPresence; + } + + public void setPresence(boolean presence) { + isPresence = presence; + } + + public boolean isAbsenceUnexcused() { + return isAbsenceUnexcused; + } + + public void setAbsenceUnexcused(boolean absenceUnexcused) { + isAbsenceUnexcused = absenceUnexcused; + } + + public boolean isAbsenceExcused() { + return isAbsenceExcused; + } + + public void setAbsenceExcused(boolean absenceExcused) { + isAbsenceExcused = absenceExcused; + } + + public boolean isUnexcusedLateness() { + return isUnexcusedLateness; + } + + public void setUnexcusedLateness(boolean unexcusedLateness) { + isUnexcusedLateness = unexcusedLateness; + } + + public boolean isAbsenceForSchoolReasons() { + return isAbsenceForSchoolReasons; + } + + public void setAbsenceForSchoolReasons(boolean absenceForSchoolReasons) { + isAbsenceForSchoolReasons = absenceForSchoolReasons; + } + + public boolean isExcusedLateness() { + return isExcusedLateness; + } + + public void setExcusedLateness(boolean excusedLateness) { + isExcusedLateness = excusedLateness; + } + + public boolean isExemption() { + return isExemption; + } + + public void setExemption(boolean exemption) { + isExemption = exemption; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Month.java b/api/src/main/java/io/github/wulkanowy/api/generic/Month.java new file mode 100644 index 000000000..e38ef3a32 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Month.java @@ -0,0 +1,26 @@ +package io.github.wulkanowy.api.generic; + +public class Month { + + private String name = ""; + + private int value = 0; + + public String getName() { + return name; + } + + public Month setName(String name) { + this.name = name; + return this; + } + + public int getValue() { + return value; + } + + public Month setValue(int value) { + this.value = value; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Subject.java b/api/src/main/java/io/github/wulkanowy/api/generic/Subject.java new file mode 100644 index 000000000..fcf27bef3 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Subject.java @@ -0,0 +1,26 @@ +package io.github.wulkanowy.api.generic; + +public class Subject { + + private int id = -1; + + private String name = ""; + + public int getId() { + return id; + } + + public Subject setId(int id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public Subject setName(String name) { + this.name = name; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Week.java b/api/src/main/java/io/github/wulkanowy/api/generic/Week.java new file mode 100644 index 000000000..1e46f3948 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Week.java @@ -0,0 +1,33 @@ +package io.github.wulkanowy.api.generic; + +import java.util.ArrayList; +import java.util.List; + +public class Week { + + private List days = new ArrayList<>(); + + private String startDayDate = ""; + + public T getDay(int index) { + return days.get(index); + } + + public List getDays() { + return days; + } + + public Week setDays(List days) { + this.days = days; + return this; + } + + public String getStartDayDate() { + return startDayDate; + } + + public Week setStartDayDate(String startDayDate) { + this.startDayDate = startDayDate; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/grades/Grade.java b/api/src/main/java/io/github/wulkanowy/api/grades/Grade.java new file mode 100644 index 000000000..564b3e47c --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/grades/Grade.java @@ -0,0 +1,112 @@ +package io.github.wulkanowy.api.grades; + +public class Grade { + + protected String value = ""; + + private String subject = ""; + + private String color = ""; + + private String symbol = ""; + + private String description = ""; + + private String weight = ""; + + private String date = ""; + + private String teacher = ""; + + private String semester = ""; + + public String getSubject() { + return subject; + } + + public Grade setSubject(String subject) { + this.subject = subject; + + return this; + } + + public String getValue() { + return value; + } + + public Grade setValue(String value) { + this.value = value; + + return this; + } + + public String getColor() { + return color; + } + + public Grade setColor(String color) { + this.color = color; + + return this; + } + + public String getSymbol() { + return symbol; + } + + public Grade setSymbol(String symbol) { + this.symbol = symbol; + + return this; + } + + public String getDescription() { + return description; + } + + public Grade setDescription(String description) { + this.description = description; + + return this; + } + + public String getWeight() { + return weight; + } + + public Grade setWeight(String weight) { + this.weight = weight; + + return this; + } + + public String getDate() { + return date; + } + + public Grade setDate(String date) { + this.date = date; + + return this; + } + + public String getTeacher() { + return teacher; + } + + public Grade setTeacher(String teacher) { + this.teacher = teacher; + + return this; + } + + public String getSemester() { + return semester; + } + + public Grade setSemester(String semester) { + this.semester = semester; + + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java b/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java new file mode 100644 index 000000000..ce8c0f770 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java @@ -0,0 +1,83 @@ +package io.github.wulkanowy.api.grades; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import io.github.wulkanowy.api.Semester; +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +public class GradesList { + + private static final String GRADES_PAGE_URL = "Oceny/Wszystkie?details=2&okres="; + + private SnP snp = null; + + private List grades = new ArrayList<>(); + + public GradesList(SnP snp) { + this.snp = snp; + } + + private String getGradesPageUrl() { + return GRADES_PAGE_URL; + } + + public List getAll() throws IOException, ParseException, VulcanException { + return getAll(""); + } + + public List getAll(String semester) throws IOException, ParseException, VulcanException { + Document gradesPage = snp.getSnPPageDocument(getGradesPageUrl() + semester); + Elements gradesRows = gradesPage.select(".ocenySzczegoly-table > tbody > tr"); + Semester currentSemester = snp.getCurrentSemester(snp.getSemesters(gradesPage)); + + for (Element row : gradesRows) { + if ("Brak ocen".equals(row.select("td:nth-child(2)").text())) { + continue; + } + + String descriptions = row.select("td:nth-child(3)").text(); + String symbol = descriptions.split(", ")[0]; + String description = descriptions.replaceFirst(symbol, "").replaceFirst(", ", ""); + + Pattern pattern = Pattern.compile("#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})"); + Matcher matcher = pattern.matcher(row.select("td:nth-child(2) span.ocenaCzastkowa") + .attr("style")); + + String color = ""; + while (matcher.find()) { + color = matcher.group(1); + } + + SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT); + Date d = sdf.parse(row.select("td:nth-child(5)").text()); + sdf.applyPattern("yyyy-MM-dd"); + + grades.add(new Grade() + .setSubject(row.select("td:nth-child(1)").text()) + .setValue(row.select("td:nth-child(2)").text()) + .setColor(color) + .setSymbol(symbol) + .setDescription(description) + .setWeight(row.select("td:nth-child(4)").text()) + .setDate(sdf.format(d)) + .setTeacher(row.select("td:nth-child(6)").text()) + .setSemester(currentSemester.getNumber()) + ); + } + + return grades; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/grades/Subject.java b/api/src/main/java/io/github/wulkanowy/api/grades/Subject.java new file mode 100644 index 000000000..c7917810e --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/grades/Subject.java @@ -0,0 +1,40 @@ +package io.github.wulkanowy.api.grades; + +public class Subject { + + private String name; + + private String predictedRating; + + private String finalRating; + + public String getName() { + return name; + } + + public Subject setName(String name) { + this.name = name; + + return this; + } + + public String getPredictedRating() { + return predictedRating; + } + + public Subject setPredictedRating(String predictedRating) { + this.predictedRating = predictedRating; + + return this; + } + + public String getFinalRating() { + return finalRating; + } + + public Subject setFinalRating(String finalRating) { + this.finalRating = finalRating; + + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/grades/SubjectsList.java b/api/src/main/java/io/github/wulkanowy/api/grades/SubjectsList.java new file mode 100644 index 000000000..993594137 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/grades/SubjectsList.java @@ -0,0 +1,41 @@ +package io.github.wulkanowy.api.grades; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +public class SubjectsList { + + private static final String SUBJECTS_PAGE_URL = "Oceny/Wszystkie?details=1"; + + private SnP snp = null; + + public SubjectsList(SnP snp) { + this.snp = snp; + } + + public List getAll() throws IOException, VulcanException { + Document subjectPage = snp.getSnPPageDocument(SUBJECTS_PAGE_URL); + + Elements rows = subjectPage.select(".ocenyZwykle-table > tbody > tr"); + + List subjects = new ArrayList<>(); + + for (Element subjectRow : rows) { + subjects.add(new Subject() + .setName(subjectRow.select("td:nth-child(1)").text()) + .setPredictedRating(subjectRow.select("td:nth-last-child(2)").text()) + .setFinalRating(subjectRow.select("td:nth-last-child(1)").text()) + ); + } + + return subjects; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/login/AccountPermissionException.java b/api/src/main/java/io/github/wulkanowy/api/login/AccountPermissionException.java new file mode 100644 index 000000000..99feb86f1 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/AccountPermissionException.java @@ -0,0 +1,6 @@ +package io.github.wulkanowy.api.login; + +import io.github.wulkanowy.api.VulcanException; + +public class AccountPermissionException extends VulcanException { +} diff --git a/api/src/main/java/io/github/wulkanowy/api/login/BadCredentialsException.java b/api/src/main/java/io/github/wulkanowy/api/login/BadCredentialsException.java new file mode 100644 index 000000000..1ac37f9c0 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/BadCredentialsException.java @@ -0,0 +1,6 @@ +package io.github.wulkanowy.api.login; + +import io.github.wulkanowy.api.VulcanException; + +public class BadCredentialsException extends VulcanException { +} diff --git a/api/src/main/java/io/github/wulkanowy/api/login/Login.java b/api/src/main/java/io/github/wulkanowy/api/login/Login.java new file mode 100644 index 000000000..7f88037de --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/Login.java @@ -0,0 +1,91 @@ +package io.github.wulkanowy.api.login; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.parser.Parser; +import org.jsoup.select.Elements; + +import java.io.IOException; + +import io.github.wulkanowy.api.Client; +import io.github.wulkanowy.api.VulcanException; + +public class Login { + + private static final String LOGIN_PAGE_URL = "{schema}://cufs.{host}/{symbol}/Account/LogOn" + + "?ReturnUrl=%2F{symbol}%2FFS%2FLS%3Fwa%3Dwsignin1.0%26wtrealm%3D" + + "{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx%26wctx%3D" + + "{schema}%253a%252f%252fuonetplus.{host}%252f{symbol}%252fLoginEndpoint.aspx"; + + private static final String LOGIN_ENDPOINT_PAGE_URL = + "{schema}://uonetplus.{host}/{symbol}/LoginEndpoint.aspx"; + + private Client client; + + private String symbol; + + public Login(Client client) { + this.client = client; + } + + public String login(String email, String password, String symbol) throws VulcanException, IOException { + String certificate = sendCredentials(email, password, symbol); + + return sendCertificate(certificate, symbol); + } + + String sendCredentials(String email, String password, String symbol) throws IOException, VulcanException { + this.symbol = symbol; + + Document html = client.postPageByUrl(LOGIN_PAGE_URL, new String[][]{ + {"LoginName", email}, + {"Password", password} + }); + + if (null != html.select(".ErrorMessage").first()) { + throw new BadCredentialsException(); + } + + return html.select("input[name=wresult]").attr("value"); + } + + String sendCertificate(String certificate, String defaultSymbol) throws IOException, VulcanException { + this.symbol = findSymbol(defaultSymbol, certificate); + client.setSymbol(this.symbol); + + String title = client.postPageByUrl(LOGIN_ENDPOINT_PAGE_URL, new String[][]{ + {"wa", "wsignin1.0"}, + {"wresult", certificate} + }).select("title").text(); + + if ("Logowanie".equals(title)) { + throw new AccountPermissionException(); + } + + if (!"Uonet+".equals(title)) { + throw new LoginErrorException(); + } + + return this.symbol; + } + + private String findSymbol(String symbol, String certificate) { + if ("Default".equals(symbol)) { + return findSymbolInCertificate(certificate); + } + + return symbol; + } + + String findSymbolInCertificate(String certificate) { + Elements els = Jsoup + .parse(certificate.replaceAll(":", ""), "", Parser.xmlParser()) + .select("[AttributeName=\"UserInstance\"] samlAttributeValue"); + + if (els.isEmpty()) { + return ""; + } + + return els.get(1).text(); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/login/LoginErrorException.java b/api/src/main/java/io/github/wulkanowy/api/login/LoginErrorException.java new file mode 100644 index 000000000..e264dc674 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/LoginErrorException.java @@ -0,0 +1,6 @@ +package io.github.wulkanowy.api.login; + +import io.github.wulkanowy.api.NotLoggedInErrorException; + +public class LoginErrorException extends NotLoggedInErrorException { +} diff --git a/api/src/main/java/io/github/wulkanowy/api/messages/BadRequestException.java b/api/src/main/java/io/github/wulkanowy/api/messages/BadRequestException.java new file mode 100644 index 000000000..ed407b4d7 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/messages/BadRequestException.java @@ -0,0 +1,6 @@ +package io.github.wulkanowy.api.messages; + +import io.github.wulkanowy.api.VulcanException; + +class BadRequestException extends VulcanException { +} diff --git a/api/src/main/java/io/github/wulkanowy/api/messages/Message.java b/api/src/main/java/io/github/wulkanowy/api/messages/Message.java new file mode 100644 index 000000000..331ec8196 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/messages/Message.java @@ -0,0 +1,30 @@ +package io.github.wulkanowy.api.messages; + +import com.google.gson.annotations.SerializedName; + +public class Message { + + @SerializedName("Nieprzeczytana") + public boolean unread; + + @SerializedName("Data") + public String date; + + @SerializedName("Tresc") + public String content; + + @SerializedName("Temat") + public String subject; + + @SerializedName("NadawcaNazwa") + public String sender; + + @SerializedName("IdWiadomosci") + public int messageID; + + @SerializedName("IdNadawca") + public int senderID; + + @SerializedName("Id") + public int id; +} diff --git a/api/src/main/java/io/github/wulkanowy/api/messages/Messages.java b/api/src/main/java/io/github/wulkanowy/api/messages/Messages.java new file mode 100644 index 000000000..eb5f8bba7 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/messages/Messages.java @@ -0,0 +1,99 @@ +package io.github.wulkanowy.api.messages; + +import com.google.gson.Gson; +import com.google.gson.JsonParseException; + +import java.io.IOException; +import java.util.List; + +import io.github.wulkanowy.api.Client; +import io.github.wulkanowy.api.NotLoggedInErrorException; +import io.github.wulkanowy.api.VulcanException; + +public class Messages { + + private static final String BASE_URL = "{schema}://uonetplus-uzytkownik.{host}/{symbol}/"; + + private static final String LIST_BASE_URL = BASE_URL + "Wiadomosc.mvc/"; + + private static final String RECEIVED_URL = LIST_BASE_URL + "GetWiadomosciOdebrane"; + + private static final String SENT_URL = LIST_BASE_URL + "GetWiadomosciWyslane"; + + private static final String DELETED_URL = LIST_BASE_URL + "GetWiadomosciUsuniete"; + + private static final String MESSAGE_URL = LIST_BASE_URL + "GetTrescWiadomosci"; + + public static final int RECEIVED_FOLDER = 1; + + public static final int SENT_FOLDER = 2; + + public static final int DELETED_FOLDER = 3; + + private static final String ERROR_TITLE = "Błąd strony"; + + private Client client; + + public Messages(Client client) { + this.client = client; + } + + public List getReceived() throws IOException, VulcanException { + return getMessages(RECEIVED_URL); + } + + public List getSent() throws IOException, VulcanException { + return getMessages(SENT_URL); + } + + public List getDeleted() throws IOException, VulcanException { + return getMessages(DELETED_URL); + } + + private List getMessages(String url) throws IOException, VulcanException { + String res = client.getJsonStringByUrl(url); + + List messages; + + try { + messages = new Gson().fromJson(res, MessagesContainer.class).data; + } catch (JsonParseException e) { + if (res.contains(ERROR_TITLE)) { + throw new BadRequestException(); + } + + throw new NotLoggedInErrorException(); + } + + return messages; + } + + public Message getMessage(int id, int folder) throws IOException, VulcanException { + String res = client.postJsonStringByUrl(MESSAGE_URL, new String[][]{ + {"idWiadomosc", String.valueOf(id)}, + {"Folder", String.valueOf(folder)} + }); + + Message message; + + try { + message = new Gson().fromJson(res, MessageContainer.class).data; + } catch (JsonParseException e) { + if (res.contains(ERROR_TITLE)) { + throw new BadRequestException(); + } + + throw new NotLoggedInErrorException(); + } + + return message; + } + + private static class MessagesContainer { + private List data; + } + + private static class MessageContainer { + private Message data; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java b/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java new file mode 100644 index 000000000..11d013429 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java @@ -0,0 +1,36 @@ +package io.github.wulkanowy.api.notes; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +public class AchievementsList { + + private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie"; + + private SnP snp = null; + + private List achievements = new ArrayList<>(); + + public AchievementsList(SnP snp) { + this.snp = snp; + } + + public List getAllAchievements() throws IOException, VulcanException { + Element pageFragment = snp.getSnPPageDocument(NOTES_PAGE_URL) + .select(".mainContainer > div").get(1); + Elements items = pageFragment.select("article"); + + for (Element item : items) { + achievements.add(item.text()); + } + + return achievements; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/notes/Note.java b/api/src/main/java/io/github/wulkanowy/api/notes/Note.java new file mode 100644 index 000000000..b1fa68b93 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/notes/Note.java @@ -0,0 +1,48 @@ +package io.github.wulkanowy.api.notes; + +public class Note { + + private String date; + + private String teacher; + + private String category; + + private String content; + + public String getDate() { + return date; + } + + public Note setDate(String date) { + this.date = date; + return this; + } + + public String getTeacher() { + return teacher; + } + + public Note setTeacher(String teacher) { + this.teacher = teacher; + return this; + } + + public String getCategory() { + return category; + } + + public Note setCategory(String category) { + this.category = category; + return this; + } + + public String getContent() { + return content; + } + + public Note setContent(String content) { + this.content = content; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java b/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java new file mode 100644 index 000000000..0c8a30b6b --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java @@ -0,0 +1,43 @@ +package io.github.wulkanowy.api.notes; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +public class NotesList { + + private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie"; + + private SnP snp = null; + + private List notes = new ArrayList<>(); + + public NotesList(SnP snp) { + this.snp = snp; + } + + public List getAllNotes() throws IOException, VulcanException { + Element pageFragment = snp.getSnPPageDocument(NOTES_PAGE_URL) + .select(".mainContainer > div").get(0); + Elements items = pageFragment.select("article"); + Elements dates = pageFragment.select("h2"); + + int index = 0; + for (Element item : items) { + notes.add(new Note() + .setDate(dates.get(index++).text()) + .setTeacher(snp.getRowDataChildValue(item, 1)) + .setCategory(snp.getRowDataChildValue(item, 2)) + .setContent(snp.getRowDataChildValue(item, 3)) + ); + } + + return notes; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/school/SchoolData.java b/api/src/main/java/io/github/wulkanowy/api/school/SchoolData.java new file mode 100644 index 000000000..f135163a2 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/school/SchoolData.java @@ -0,0 +1,59 @@ +package io.github.wulkanowy.api.school; + +public class SchoolData { + + private String name = ""; + + private String address = ""; + + private String phoneNumber = ""; + + private String headmaster = ""; + + private String[] pedagogue; + + public String getName() { + return name; + } + + public SchoolData setName(String name) { + this.name = name; + return this; + } + + public String getAddress() { + return address; + } + + public SchoolData setAddress(String address) { + this.address = address; + return this; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public SchoolData setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + return this; + } + + public String getHeadmaster() { + return headmaster; + } + + public SchoolData setHeadmaster(String headmaster) { + this.headmaster = headmaster; + return this; + } + + public String[] getPedagogues() { + return pedagogue; + } + + public SchoolData setPedagogue(String[] pedagogue) { + this.pedagogue = pedagogue; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java b/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java new file mode 100644 index 000000000..51a7278bd --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java @@ -0,0 +1,31 @@ +package io.github.wulkanowy.api.school; + +import org.jsoup.nodes.Element; + +import java.io.IOException; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +public class SchoolInfo { + + private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele"; + + private SnP snp = null; + + public SchoolInfo(SnP snp) { + this.snp = snp; + } + + public SchoolData getSchoolData() throws IOException, VulcanException { + Element e = snp.getSnPPageDocument(SCHOOL_PAGE_URL) + .select(".mainContainer > article").get(0); + + return new SchoolData() + .setName(snp.getRowDataChildValue(e, 1)) + .setAddress(snp.getRowDataChildValue(e, 2)) + .setPhoneNumber(snp.getRowDataChildValue(e, 3)) + .setHeadmaster(snp.getRowDataChildValue(e, 4)) + .setPedagogue(snp.getRowDataChildValue(e, 5).split(", ")); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/school/Subject.java b/api/src/main/java/io/github/wulkanowy/api/school/Subject.java new file mode 100644 index 000000000..0a3c0957d --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/school/Subject.java @@ -0,0 +1,26 @@ +package io.github.wulkanowy.api.school; + +public class Subject { + + private String name = ""; + + private String[] teachers; + + public String getName() { + return name; + } + + public Subject setName(String name) { + this.name = name; + return this; + } + + public String[] getTeachers() { + return teachers; + } + + public Subject setTeachers(String[] teachers) { + this.teachers = teachers; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/school/TeachersData.java b/api/src/main/java/io/github/wulkanowy/api/school/TeachersData.java new file mode 100644 index 000000000..7d41162e5 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/school/TeachersData.java @@ -0,0 +1,39 @@ +package io.github.wulkanowy.api.school; + +import java.util.List; + +public class TeachersData { + + private String className = ""; + + private String[] classTeacher; + + private List subjects; + + public String getClassName() { + return className; + } + + public TeachersData setClassName(String className) { + this.className = className; + return this; + } + + public String[] getClassTeacher() { + return classTeacher; + } + + public TeachersData setClassTeacher(String[] classTeacher) { + this.classTeacher = classTeacher; + return this; + } + + public List getSubjects() { + return subjects; + } + + public TeachersData setSubjects(List subjects) { + this.subjects = subjects; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java b/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java new file mode 100644 index 000000000..bbf5f5d7f --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java @@ -0,0 +1,43 @@ +package io.github.wulkanowy.api.school; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +public class TeachersInfo { + + private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele"; + + private SnP snp = null; + + public TeachersInfo(SnP snp) { + this.snp = snp; + } + + public TeachersData getTeachersData() throws IOException, VulcanException { + Document doc = snp.getSnPPageDocument(SCHOOL_PAGE_URL); + Elements rows = doc.select(".mainContainer > table tbody tr"); + String description = doc.select(".mainContainer > p").first().text(); + + List subjects = new ArrayList<>(); + + for (Element subject : rows) { + subjects.add(new Subject() + .setName(subject.select("td").get(1).text()) + .setTeachers(subject.select("td").get(2).text().split(", ")) + ); + } + + return new TeachersData() + .setClassName(description.split(", ")[0].split(": ")[1].trim()) + .setClassTeacher(description.split("Wychowawcy:")[1].trim().split(", ")) + .setSubjects(subjects); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/timetable/LessonTypes.java b/api/src/main/java/io/github/wulkanowy/api/timetable/LessonTypes.java new file mode 100644 index 000000000..be1f44e91 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/timetable/LessonTypes.java @@ -0,0 +1,16 @@ +package io.github.wulkanowy.api.timetable; + +class LessonTypes { + + static final String CLASS_PLANNING = "x-treelabel-ppl"; + + static final String CLASS_REALIZED = "x-treelabel-rlz"; + + static final String CLASS_MOVED_OR_CANCELED = "x-treelabel-inv"; + + static final String CLASS_NEW_MOVED_IN_OR_CHANGED = "x-treelabel-zas"; + + private LessonTypes() { + throw new IllegalStateException("Utility class"); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java b/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java new file mode 100644 index 000000000..f575edb38 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java @@ -0,0 +1,216 @@ +package io.github.wulkanowy.api.timetable; + +import org.apache.commons.lang3.StringUtils; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; +import io.github.wulkanowy.api.generic.Day; +import io.github.wulkanowy.api.generic.Lesson; +import io.github.wulkanowy.api.generic.Week; + +public class Timetable { + + private static final String TIMETABLE_PAGE_URL = "Lekcja.mvc/PlanLekcji?data="; + + private SnP snp; + + public Timetable(SnP snp) { + this.snp = snp; + } + + public Week getWeekTable() throws IOException, ParseException, VulcanException { + return getWeekTable(""); + } + + public Week getWeekTable(final String tick) throws IOException, ParseException, VulcanException { + Element table = snp.getSnPPageDocument(TIMETABLE_PAGE_URL + tick) + .select(".mainContainer .presentData").first(); + + List days = getDays(table.select("thead th")); + + setLessonToDays(table, days); + + return new Week() + .setStartDayDate(days.get(0).getDate()) + .setDays(days); + } + + private List getDays(Elements tableHeaderCells) throws ParseException { + List days = new ArrayList<>(); + + for (int i = 2; i < 7; i++) { + String[] dayHeaderCell = tableHeaderCells.get(i).html().split("
"); + + SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT); + Date d = sdf.parse(dayHeaderCell[1].trim()); + sdf.applyPattern("yyyy-MM-dd"); + + Day day = new Day(); + day.setDayName(dayHeaderCell[0]); + day.setDate(sdf.format(d)); + + if (tableHeaderCells.get(i).hasClass("free-day")) { + day.setFreeDay(true); + day.setFreeDayName(dayHeaderCell[2]); + } + + days.add(day); + } + + return days; + } + + private void setLessonToDays(Element table, List days) { + for (Element row : table.select("tbody tr")) { + Elements hours = row.select("td"); + + // fill hours in day + for (int i = 2; i < hours.size(); i++) { + Lesson lesson = new Lesson(); + + String[] startEndEnd = hours.get(1).text().split(" "); + lesson.setStartTime(startEndEnd[0]); + lesson.setEndTime(startEndEnd[1]); + lesson.setDate(days.get(i - 2).getDate()); + lesson.setNumber(hours.get(0).text()); + + addLessonDetails(lesson, hours.get(i).select("div")); + + days.get(i - 2).setLesson(lesson); + } + } + } + + private void addLessonDetails(Lesson lesson, Elements e) { + moveWarningToLessonNode(e); + + switch (e.size()) { + case 1: + addLessonInfoFromElement(lesson, e.first()); + break; + case 2: + addLessonInfoFromElement(lesson, e.last()); + break; + case 3: + addLessonInfoFromElement(lesson, e.get(1)); + break; + default: + lesson.setEmpty(true); + break; + } + } + + private void moveWarningToLessonNode(Elements e) { + Elements warn = e.select(".uwaga-panel"); + + if (!warn.isEmpty()) { + e.select(".x-treelabel-rlz").last().text("(" + warn.text() + ")"); + e.remove(1); + } + } + + private void addLessonInfoFromElement(Lesson lesson, Element e) { + Elements spans = e.select("span"); + + addTypeInfo(lesson, spans); + addNormalLessonInfo(lesson, spans); + addChangesInfo(lesson, spans); + addGroupLessonInfo(lesson, spans); + } + + private void addTypeInfo(Lesson lesson, Elements spans) { + if (spans.first().hasClass(LessonTypes.CLASS_PLANNING)) { + lesson.setPlanning(true); + } + + if (spans.first().hasClass(LessonTypes.CLASS_MOVED_OR_CANCELED)) { + lesson.setMovedOrCanceled(true); + } + + if (spans.first().hasClass(LessonTypes.CLASS_NEW_MOVED_IN_OR_CHANGED)) { + lesson.setNewMovedInOrChanged(true); + } + + if (spans.last().hasClass(LessonTypes.CLASS_REALIZED) || "".equals(spans.first().attr("class"))) { + lesson.setRealized(true); + } + } + + private void addNormalLessonInfo(Lesson lesson, Elements spans) { + if (3 == spans.size()) { + lesson.setSubject(spans.get(0).text()); + lesson.setTeacher(spans.get(1).text()); + lesson.setRoom(spans.get(2).text()); + } + } + + private void addChangesInfo(Lesson lesson, Elements spans) { + if (!spans.last().hasClass(LessonTypes.CLASS_REALIZED)) { + return; + } + + if (7 == spans.size()) { + lesson.setSubject(spans.get(3).text()); + lesson.setTeacher(spans.get(4).text()); + lesson.setRoom(spans.get(5).text()); + lesson.setMovedOrCanceled(false); + lesson.setNewMovedInOrChanged(true); + lesson.setDescription(StringUtils.substringBetween(spans.last().text(), "(", ")") + + " (poprzednio: " + spans.get(0).text() + ")"); + } else if (9 == spans.size()) { + String[] subjectAndGroupInfo = getLessonAndGroupInfoFromSpan(spans.get(4)); + lesson.setSubject(subjectAndGroupInfo[0]); + lesson.setGroupName(subjectAndGroupInfo[1]); + lesson.setTeacher(spans.get(6).text()); + lesson.setRoom(spans.get(7).text()); + lesson.setMovedOrCanceled(false); + lesson.setNewMovedInOrChanged(true); + lesson.setDivisionIntoGroups(true); + lesson.setDescription(StringUtils.substringBetween(spans.last().text(), "(", ")") + + " (poprzednio: " + getLessonAndGroupInfoFromSpan(spans.get(0))[0] + ")"); + } else if (4 <= spans.size()) { + lesson.setSubject(spans.get(0).text()); + lesson.setTeacher(spans.get(1).text()); + lesson.setRoom(spans.get(2).text()); + lesson.setDescription(StringUtils.substringBetween(spans.last().text(), "(", ")")); + } + } + + private void addGroupLessonInfo(Lesson lesson, Elements spans) { + if (4 == spans.size() && !spans.last().hasClass(LessonTypes.CLASS_REALIZED)) { + lesson.setRoom(spans.last().text()); + } + + if ((4 == spans.size() && !spans.last().hasClass(LessonTypes.CLASS_REALIZED) || 5 == spans.size())) { + String[] subjectAndGroupInfo = getLessonAndGroupInfoFromSpan(spans.get(0)); + lesson.setSubject(subjectAndGroupInfo[0]); + lesson.setGroupName(subjectAndGroupInfo[1]); + lesson.setTeacher(spans.get(2).text()); + lesson.setDivisionIntoGroups(true); + } + + if (5 == spans.size()) { + lesson.setRoom(spans.get(3).text()); + } + } + + private String[] getLessonAndGroupInfoFromSpan(Element span) { + String[] subjectNameArray = span.text().split(" "); + String groupName = subjectNameArray[subjectNameArray.length - 1]; + + return new String[]{ + span.text().replace(" " + groupName, ""), + StringUtils.substringBetween(groupName, "[", "]") + }; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/AddressData.java b/api/src/main/java/io/github/wulkanowy/api/user/AddressData.java new file mode 100644 index 000000000..3d6fc6d54 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/AddressData.java @@ -0,0 +1,40 @@ +package io.github.wulkanowy.api.user; + +public class AddressData { + + private String address = ""; + + private String registeredAddress = ""; + + private String correspondenceAddress = ""; + + public String getAddress() { + return address; + } + + public AddressData setAddress(String address) { + this.address = address; + + return this; + } + + public String getRegisteredAddress() { + return registeredAddress; + } + + public AddressData setRegisteredAddress(String registeredAddress) { + this.registeredAddress = registeredAddress; + + return this; + } + + public String getCorrespondenceAddress() { + return correspondenceAddress; + } + + public AddressData setCorrespondenceAddress(String correspondenceAddress) { + this.correspondenceAddress = correspondenceAddress; + + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/BasicInformation.java b/api/src/main/java/io/github/wulkanowy/api/user/BasicInformation.java new file mode 100644 index 000000000..4edc5117d --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/BasicInformation.java @@ -0,0 +1,70 @@ +package io.github.wulkanowy.api.user; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; + +import java.io.IOException; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +public class BasicInformation { + + private static final String STUDENT_DATA_PAGE_URL = "Uczen.mvc/DanePodstawowe"; + + private static final String CONTENT_QUERY = ".mainContainer > article"; + + private Document studentDataPageDocument; + + private SnP snp; + + public BasicInformation(SnP snp) { + this.snp = snp; + } + + public Document getStudentDataPageDocument() throws IOException, VulcanException { + if (null == studentDataPageDocument) { + studentDataPageDocument = snp.getSnPPageDocument(STUDENT_DATA_PAGE_URL); + } + + return studentDataPageDocument; + } + + public PersonalData getPersonalData() throws IOException, VulcanException { + Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(0); + + String name = snp.getRowDataChildValue(e, 1); + String[] names = name.split(" "); + + return new PersonalData() + .setName(name) + .setFirstName(names[0]) + .setSurname(names[names.length - 1]) + .setFirstAndLastName(names[0] + " " + names[names.length - 1]) + .setDateAndBirthPlace(snp.getRowDataChildValue(e, 2)) + .setPesel(snp.getRowDataChildValue(e, 3)) + .setGender(snp.getRowDataChildValue(e, 4)) + .setPolishCitizenship("Tak".equals(snp.getRowDataChildValue(e, 5))) + .setFamilyName(snp.getRowDataChildValue(e, 6)) + .setParentsNames(snp.getRowDataChildValue(e, 7)); + } + + public AddressData getAddressData() throws IOException, VulcanException { + Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(1); + + return new AddressData() + .setAddress(snp.getRowDataChildValue(e, 1)) + .setRegisteredAddress(snp.getRowDataChildValue(e, 2)) + .setCorrespondenceAddress(snp.getRowDataChildValue(e, 3)); + + } + + public ContactDetails getContactDetails() throws IOException, VulcanException { + Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(2); + + return new ContactDetails() + .setPhoneNumber(snp.getRowDataChildValue(e, 1)) + .setCellPhoneNumber(snp.getRowDataChildValue(e, 2)) + .setEmail(snp.getRowDataChildValue(e, 3)); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/ContactDetails.java b/api/src/main/java/io/github/wulkanowy/api/user/ContactDetails.java new file mode 100644 index 000000000..c8556944e --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/ContactDetails.java @@ -0,0 +1,37 @@ +package io.github.wulkanowy.api.user; + +public class ContactDetails { + + private String phoneNumber = ""; + + private String cellPhoneNumber = ""; + + private String email = ""; + + public String getPhoneNumber() { + return phoneNumber; + } + + public ContactDetails setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + return this; + } + + public String getCellPhoneNumber() { + return cellPhoneNumber; + } + + public ContactDetails setCellPhoneNumber(String cellPhoneNumber) { + this.cellPhoneNumber = cellPhoneNumber; + return this; + } + + public String getEmail() { + return email; + } + + public ContactDetails setEmail(String email) { + this.email = email; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/FamilyInformation.java b/api/src/main/java/io/github/wulkanowy/api/user/FamilyInformation.java new file mode 100644 index 000000000..f5a459ee8 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/FamilyInformation.java @@ -0,0 +1,41 @@ +package io.github.wulkanowy.api.user; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +public class FamilyInformation { + + private static final String STUDENT_DATA_PAGE_URL = "Uczen.mvc/DanePodstawowe"; + + private SnP snp; + + public FamilyInformation(SnP snp) { + this.snp = snp; + } + + public List getFamilyMembers() throws IOException, VulcanException { + Elements membersElements = snp.getSnPPageDocument(STUDENT_DATA_PAGE_URL) + .select(".mainContainer > article:nth-of-type(n+4)"); + + List familyMembers = new ArrayList<>(); + + for (Element e : membersElements) { + familyMembers.add(new FamilyMember() + .setName(snp.getRowDataChildValue(e, 1)) + .setKinship(snp.getRowDataChildValue(e, 2)) + .setAddress(snp.getRowDataChildValue(e, 3)) + .setTelephones(snp.getRowDataChildValue(e, 4)) + .setEmail(snp.getRowDataChildValue(e, 5)) + ); + } + + return familyMembers; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/FamilyMember.java b/api/src/main/java/io/github/wulkanowy/api/user/FamilyMember.java new file mode 100644 index 000000000..dfb966745 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/FamilyMember.java @@ -0,0 +1,59 @@ +package io.github.wulkanowy.api.user; + +public class FamilyMember { + + private String name = ""; + + private String kinship = ""; + + private String address = ""; + + private String telephones = ""; + + private String email = ""; + + public String getName() { + return name; + } + + public FamilyMember setName(String name) { + this.name = name; + return this; + } + + public String getKinship() { + return kinship; + } + + public FamilyMember setKinship(String kinship) { + this.kinship = kinship; + return this; + } + + public String getAddress() { + return address; + } + + public FamilyMember setAddress(String address) { + this.address = address; + return this; + } + + public String getTelephones() { + return telephones; + } + + public FamilyMember setTelephones(String telephones) { + this.telephones = telephones; + return this; + } + + public String getEmail() { + return email; + } + + public FamilyMember setEmail(String email) { + this.email = email; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/PersonalData.java b/api/src/main/java/io/github/wulkanowy/api/user/PersonalData.java new file mode 100644 index 000000000..d2991e613 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/PersonalData.java @@ -0,0 +1,114 @@ +package io.github.wulkanowy.api.user; + +public class PersonalData { + + private String name = ""; + + private String firstName = ""; + + private String surname = ""; + + private String firstAndLastName = ""; + + private String dateAndBirthPlace = ""; + + private String pesel = ""; + + private String gender = ""; + + private boolean isPolishCitizenship; + + private String familyName = ""; + + private String parentsNames = ""; + + public String getName() { + return name; + } + + public PersonalData setName(String name) { + this.name = name; + return this; + } + + public String getFirstName() { + return firstName; + } + + public PersonalData setFirstName(String firstName) { + this.firstName = firstName; + return this; + } + + public String getSurname() { + return surname; + } + + public PersonalData setSurname(String surname) { + this.surname = surname; + return this; + } + + public String getFirstAndLastName() { + return firstAndLastName; + } + + public PersonalData setFirstAndLastName(String firstAndLastName) { + this.firstAndLastName = firstAndLastName; + return this; + } + + public String getDateAndBirthPlace() { + return dateAndBirthPlace; + } + + public PersonalData setDateAndBirthPlace(String dateAndBirthPlace) { + this.dateAndBirthPlace = dateAndBirthPlace; + return this; + } + + public String getPesel() { + return pesel; + } + + public PersonalData setPesel(String pesel) { + this.pesel = pesel; + return this; + } + + public String getGender() { + return gender; + } + + public PersonalData setGender(String gender) { + this.gender = gender; + return this; + } + + public boolean isPolishCitizenship() { + return isPolishCitizenship; + } + + public PersonalData setPolishCitizenship(boolean polishCitizenship) { + isPolishCitizenship = polishCitizenship; + return this; + } + + public String getFamilyName() { + return familyName; + } + + public PersonalData setFamilyName(String familyName) { + this.familyName = familyName; + return this; + } + + public String getParentsNames() { + return parentsNames; + } + + public PersonalData setParentsNames(String parentsNames) { + this.parentsNames = parentsNames; + return this; + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/ClientTest.java b/api/src/test/java/io/github/wulkanowy/api/ClientTest.java new file mode 100644 index 000000000..4aa1be915 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/ClientTest.java @@ -0,0 +1,80 @@ +package io.github.wulkanowy.api; + +import org.hamcrest.CoreMatchers; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.junit.Assert; +import org.junit.Test; + +import io.github.wulkanowy.api.login.Login; + +public class ClientTest { + + private String getFixtureAsString(String fixtureFileName) { + return FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName)); + } + + @Test + public void setFullEndpointInfoTest() throws Exception { + Client client = new Client("http://fakelog.net\\\\admin", "pass", "Default"); + + Assert.assertEquals("fakelog.net", client.getHost()); + Assert.assertEquals("Default", client.getSymbol()); + } + + @Test + public void checkForNoErrorsTest() throws Exception { + Client client = new Client("", "", ""); + + Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-success.html")); + + Assert.assertEquals(doc, client.checkForErrors(doc)); + } + + @Test(expected = VulcanOfflineException.class) + public void checkForErrorsOffline() throws Exception { + Client client = new Client("", "", ""); + + Document doc = Jsoup.parse(getFixtureAsString("login/PrzerwaTechniczna.html")); + + client.checkForErrors(doc); + } + + @Test(expected = NotLoggedInErrorException.class) + public void checkForErrors() throws Exception { + Client client = new Client("", "", ""); + + Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-notLoggedIn.html")); + + client.checkForErrors(doc); + } + + @Test + public void getClientTest() throws Exception { + Client client = new Client("", "", ""); + + Assert.assertThat(client.getLogin(), CoreMatchers.instanceOf(Login.class)); + } + + @Test + public void getClientTwiceTest() throws Exception { + Client client = new Client("", "", ""); + + Assert.assertEquals(client.getLogin(), client.getLogin()); + } + + @Test + public void getFilledUrlTest() throws Exception { + Client client = new Client("http://fakelog.cf\\\\admin", "", "symbol123"); + + Assert.assertEquals("http://uonetplus.fakelog.cf/symbol123/LoginEndpoint.aspx", + client.getFilledUrl("{schema}://uonetplus.{host}/{symbol}/LoginEndpoint.aspx")); + } + + @Test + public void getSymbolTest() throws Exception { + Client client = new Client("", "", "symbol4321"); + + Assert.assertEquals("symbol4321", client.getSymbol()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/FixtureHelper.java b/api/src/test/java/io/github/wulkanowy/api/FixtureHelper.java new file mode 100644 index 000000000..94dd47b87 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/FixtureHelper.java @@ -0,0 +1,12 @@ +package io.github.wulkanowy.api; + +import java.io.InputStream; +import java.util.Scanner; + +public class FixtureHelper { + + public static String getAsString(InputStream inputStream) { + Scanner s = new Scanner(inputStream).useDelimiter("\\A"); + return s.hasNext() ? s.next() : ""; + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java new file mode 100644 index 000000000..5cde9b644 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java @@ -0,0 +1,123 @@ +package io.github.wulkanowy.api; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; + +public class StudentAndParentTest { + + private Client client; + + @Before + public void setUp() throws Exception { + String input = FixtureHelper.getAsString( + getClass().getResourceAsStream("OcenyWszystkie-semester.html")); + Document gradesPageDocument = Jsoup.parse(input); + + client = Mockito.mock(Client.class); + Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(gradesPageDocument); + } + + @Test + public void snpTest() throws Exception { + StudentAndParent snp = new StudentAndParent(client, "id123"); + Assert.assertEquals("id123", snp.getId()); + } + + @Test + public void getSnpPageUrlWithIdTest() throws Exception { + Assert.assertEquals("{schema}://uonetplus-opiekun.{host}/{symbol}/123456/", + (new StudentAndParent(client, "123456")).getSnpHomePageUrl()); + } + + @Test + public void getSnpPageUrlWithoutIdTest() throws Exception { + String input = FixtureHelper.getAsString(getClass().getResourceAsStream("Start.html")); + Document startPageDocument = Jsoup.parse(input); + + Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); + Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(startPageDocument); + StudentAndParent snp = new StudentAndParent(client, null); + + Assert.assertEquals("https://uonetplus-opiekun.vulcan.net.pl/symbol/534213/Start/Index/", + snp.getSnpHomePageUrl()); + } + + @Test(expected = NotLoggedInErrorException.class) + public void getSnpPageUrlWithWrongPage() throws Exception { + Document wrongPageDocument = Jsoup.parse( + FixtureHelper.getAsString(getClass().getResourceAsStream("OcenyWszystkie-semester.html")) + ); + + Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(wrongPageDocument); + StudentAndParent snp = new StudentAndParent(client, null); + + snp.getSnpHomePageUrl(); + } + + @Test + public void getExtractedIDStandardTest() throws Exception { + Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); + StudentAndParent snp = new StudentAndParent(client, "symbol"); + Assert.assertEquals("123456", snp.getExtractedIdFromUrl("https://uonetplus-opiekun" + + ".vulcan.net.pl/powiat/123456/Start/Index/")); + } + + @Test + public void getExtractedIDDemoTest() throws Exception { + Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); + StudentAndParent snp = new StudentAndParent(client, "symbol"); + Assert.assertEquals("demo12345", + snp.getExtractedIdFromUrl("https://uonetplus-opiekun.vulcan.net.pl/demoupowiat/demo12345/Start/Index/")); + } + + @Test(expected = NotLoggedInErrorException.class) + public void getExtractedIDNotLoggedTest() throws Exception { + Mockito.when(client.getHost()).thenReturn("vulcan.net.pl"); + StudentAndParent snp = new StudentAndParent(client, "symbol"); + Assert.assertEquals("123", + snp.getExtractedIdFromUrl("https://uonetplus.vulcan.net.pl/powiat/")); + } + + @Test + public void getSemestersTest() throws Exception { + SnP snp = new StudentAndParent(client, "123456"); + List semesters = snp.getSemesters(); + + Assert.assertEquals(2, semesters.size()); + + Assert.assertEquals("1", semesters.get(0).getId()); + Assert.assertEquals("1234", semesters.get(0).getNumber()); + Assert.assertFalse(semesters.get(0).isCurrent()); + + Assert.assertEquals("2", semesters.get(1).getId()); + Assert.assertEquals("1235", semesters.get(1).getNumber()); + Assert.assertTrue(semesters.get(1).isCurrent()); + } + + @Test + public void getCurrentSemesterTest() throws Exception { + List semesters = new ArrayList<>(); + semesters.add(new Semester().setNumber("1500100900").setId("1").setCurrent(false)); + semesters.add(new Semester().setNumber("1500100901").setId("2").setCurrent(true)); + + SnP snp = new StudentAndParent(client, ""); + Assert.assertTrue(snp.getCurrentSemester(semesters).isCurrent()); + Assert.assertEquals("2", snp.getCurrentSemester(semesters).getId()); + Assert.assertEquals("1500100901", snp.getCurrentSemester(semesters).getNumber()); + } + + @Test + public void getCurrentSemesterFromEmptyTest() throws Exception { + SnP snp = new StudentAndParent(client, ""); + List semesters = new ArrayList<>(); + + Assert.assertNull(snp.getCurrentSemester(semesters)); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java new file mode 100644 index 000000000..b18076cfa --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java @@ -0,0 +1,26 @@ +package io.github.wulkanowy.api; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.mockito.Mockito; + +public abstract class StudentAndParentTestCase { + + protected StudentAndParent getSnp(String fixtureFileName) throws Exception { + String input = FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName)); + + Document tablePageDocument = Jsoup.parse(input); + + StudentAndParent snp = Mockito.mock(StudentAndParent.class); + Mockito.when(snp.getSnPPageDocument(Mockito.anyString())) + .thenReturn(tablePageDocument); + Mockito.when(snp.getSemesters(Mockito.any(Document.class))).thenCallRealMethod(); + Mockito.when(snp.getCurrentSemester(Mockito.anyList())) + .thenCallRealMethod(); + Mockito.when(snp.getRowDataChildValue(Mockito.any(Element.class), + Mockito.anyInt())).thenCallRealMethod(); + + return snp; + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java b/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java new file mode 100644 index 000000000..bda66e8d1 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java @@ -0,0 +1,31 @@ +package io.github.wulkanowy.api; + +import org.hamcrest.CoreMatchers; +import org.junit.Assert; +import org.junit.Test; + +public class VulcanTest { + + @Test(expected = NotLoggedInErrorException.class) + public void getClientWithoutLoginTest() throws Exception { + Vulcan vulcan = new Vulcan(); + + vulcan.getClient(); + } + + @Test + public void getClientTest() throws Exception { + Vulcan vulcan = new Vulcan(); + vulcan.setCredentials("email", "password", "symbol", null); + + Assert.assertThat(vulcan.getClient(), CoreMatchers.instanceOf(Client.class)); + } + + @Test + public void getClientTwiceTest() throws Exception { + Vulcan vulcan = new Vulcan(); + vulcan.setCredentials("email", "password", "symbol", null); + + Assert.assertEquals(vulcan.getClient(), vulcan.getClient()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceStatisticsTest.java b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceStatisticsTest.java new file mode 100644 index 000000000..ca50f3789 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceStatisticsTest.java @@ -0,0 +1,144 @@ +package io.github.wulkanowy.api.attendance; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; +import io.github.wulkanowy.api.generic.Month; + +public class AttendanceStatisticsTest extends StudentAndParentTestCase { + + private AttendanceStatistics excellent; + + private AttendanceStatistics full; + + @Before + public void setUp() throws Exception { + this.excellent = new AttendanceStatistics(getSnp("Frekwencja-excellent.html")); + this.full = new AttendanceStatistics(getSnp("Frekwencja-full.html")); + } + + @Test + public void getSubjectList() throws Exception { + Assert.assertEquals(26, excellent.getSubjectList().size()); + Assert.assertEquals(23, full.getSubjectList().size()); + } + + @Test + public void getSubjectListId() throws Exception { + Assert.assertEquals(-1, excellent.getSubjectList().get(0).getId()); + Assert.assertEquals(63, excellent.getSubjectList().get(10).getId()); + Assert.assertEquals(0, excellent.getSubjectList().get(25).getId()); + + Assert.assertEquals(-1, full.getSubjectList().get(0).getId()); + Assert.assertEquals(108, full.getSubjectList().get(14).getId()); + Assert.assertEquals(492, full.getSubjectList().get(21).getId()); + } + + @Test + public void getSubjectListName() throws Exception { + Assert.assertEquals("Wszystkie", excellent.getSubjectList().get(0).getName()); + Assert.assertEquals("Fizyka", excellent.getSubjectList().get(8).getName()); + Assert.assertEquals("Sieci komputerowe i administrowanie sieciami", + excellent.getSubjectList().get(21).getName()); + + Assert.assertEquals("Praktyka zawodowa", full.getSubjectList().get(11).getName()); + Assert.assertEquals("Użytkowanie urządzeń peryferyjnych komputera", + full.getSubjectList().get(16).getName()); + Assert.assertEquals("Brak opisu lekcji", full.getSubjectList().get(22).getName()); + } + + @Test + public void getTypesTotal() throws Exception { + Assert.assertEquals(100.0, excellent.getTypesTable().getTotal(), 0); + Assert.assertEquals(80.94, full.getTypesTable().getTotal(), 0); + } + + @Test + public void getTypeName() throws Exception { + List typeList1 = excellent.getTypesTable().getTypeList(); + Assert.assertEquals("Obecność", typeList1.get(0).getName()); + Assert.assertEquals("Nieobecność nieusprawiedliwiona", typeList1.get(1).getName()); + Assert.assertEquals("Nieobecność usprawiedliwiona", typeList1.get(2).getName()); + Assert.assertEquals("Nieobecność z przyczyn szkolnych", typeList1.get(3).getName()); + + List typeList2 = full.getTypesTable().getTypeList(); + Assert.assertEquals("Spóźnienie nieusprawiedliwione", typeList2.get(4).getName()); + Assert.assertEquals("Spóźnienie usprawiedliwione", typeList2.get(5).getName()); + Assert.assertEquals("Zwolnienie", typeList2.get(6).getName()); + } + + @Test + public void getTypeTotal() throws Exception { + List typeList1 = excellent.getTypesTable().getTypeList(); + Assert.assertEquals(1211, typeList1.get(0).getTotal()); + Assert.assertEquals(0, typeList1.get(1).getTotal()); + Assert.assertEquals(0, typeList1.get(2).getTotal()); + Assert.assertEquals(0, typeList1.get(3).getTotal()); + Assert.assertEquals(0, typeList1.get(4).getTotal()); + Assert.assertEquals(0, typeList1.get(5).getTotal()); + Assert.assertEquals(0, typeList1.get(6).getTotal()); + + List typeList2 = full.getTypesTable().getTypeList(); + Assert.assertEquals(822, typeList2.get(0).getTotal()); + Assert.assertEquals(6, typeList2.get(1).getTotal()); + Assert.assertEquals(192, typeList2.get(2).getTotal()); + Assert.assertEquals(7, typeList2.get(3).getTotal()); + Assert.assertEquals(12, typeList2.get(4).getTotal()); + Assert.assertEquals(1, typeList2.get(5).getTotal()); + Assert.assertEquals(2, typeList2.get(6).getTotal()); + } + + @Test + public void getTypeList() throws Exception { + List typesList1 = excellent.getTypesTable().getTypeList(); + Assert.assertEquals(12, typesList1.get(0).getMonthList().size()); + Assert.assertEquals(12, typesList1.get(5).getMonthList().size()); + + List typesList2 = full.getTypesTable().getTypeList(); + Assert.assertEquals(12, typesList2.get(0).getMonthList().size()); + Assert.assertEquals(12, typesList2.get(5).getMonthList().size()); + } + + @Test + public void getMonthList() throws Exception { + List typeList1 = excellent.getTypesTable().getTypeList(); + Assert.assertEquals(12, typeList1.get(0).getMonthList().size()); + + List typeList2 = full.getTypesTable().getTypeList(); + Assert.assertEquals(12, typeList2.get(0).getMonthList().size()); + } + + @Test + public void getMonthName() throws Exception { + List monthsList1 = excellent.getTypesTable().getTypeList().get(0).getMonthList(); + Assert.assertEquals("IX", monthsList1.get(0).getName()); + Assert.assertEquals("III", monthsList1.get(6).getName()); + Assert.assertEquals("VIII", monthsList1.get(11).getName()); + + List monthsList2 = full.getTypesTable().getTypeList().get(0).getMonthList(); + Assert.assertEquals("XI", monthsList2.get(2).getName()); + Assert.assertEquals("II", monthsList2.get(5).getName()); + Assert.assertEquals("VI", monthsList2.get(9).getName()); + } + + @Test + public void getMonthValue() throws Exception { + List monthsList1 = excellent.getTypesTable().getTypeList().get(0).getMonthList(); + Assert.assertEquals(142, monthsList1.get(0).getValue()); + Assert.assertEquals(131, monthsList1.get(4).getValue()); + Assert.assertEquals(139, monthsList1.get(7).getValue()); + Assert.assertEquals(114, monthsList1.get(9).getValue()); + Assert.assertEquals(0, monthsList1.get(11).getValue()); + + List typeList1 = full.getTypesTable().getTypeList(); + Assert.assertEquals(135, typeList1.get(0).getMonthList().get(0).getValue()); + Assert.assertEquals(7, typeList1.get(3).getMonthList().get(5).getValue()); + Assert.assertEquals(1, typeList1.get(5).getMonthList().get(0).getValue()); + Assert.assertEquals(27, typeList1.get(2).getMonthList().get(9).getValue()); + Assert.assertEquals(0, typeList1.get(0).getMonthList().get(11).getValue()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java new file mode 100644 index 000000000..40ff4f065 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java @@ -0,0 +1,165 @@ +package io.github.wulkanowy.api.attendance; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class AttendanceTableTest extends StudentAndParentTestCase { + + private AttendanceTable excellent; + + private AttendanceTable full; + + @Before + public void setUp() throws Exception { + excellent = new AttendanceTable(getSnp("Frekwencja-excellent.html")); + full = new AttendanceTable(getSnp("Frekwencja-full.html")); + } + + @Test + public void getWeekStartByDate() throws Exception { + Assert.assertEquals("2015-08-31", excellent.getWeekTable().getStartDayDate()); + Assert.assertEquals("2016-09-05", full.getWeekTable().getStartDayDate()); + } + + @Test + public void getWeekDaysNumber() throws Exception { + Assert.assertEquals(5, excellent.getWeekTable().getDays().size()); + Assert.assertEquals(5, full.getWeekTable().getDays().size()); + } + + @Test + public void getDayLessonsNumber() throws Exception { + Assert.assertEquals(14, excellent.getWeekTable().getDay(0).getLessons().size()); + Assert.assertEquals(14, full.getWeekTable().getDay(0).getLessons().size()); + } + + @Test + public void getDayDate() throws Exception { + Assert.assertEquals("2015-08-31", excellent.getWeekTable().getDay(0).getDate()); + Assert.assertEquals("2015-09-02", excellent.getWeekTable().getDay(2).getDate()); + Assert.assertEquals("2015-09-04", excellent.getWeekTable().getDay(4).getDate()); + + Assert.assertEquals("2016-09-05", full.getWeekTable().getDay(0).getDate()); + Assert.assertEquals("2016-09-07", full.getWeekTable().getDay(2).getDate()); + Assert.assertEquals("2016-09-09", full.getWeekTable().getDay(4).getDate()); + } + + @Test + public void getLessonSubject() throws Exception { + Assert.assertEquals("", + excellent.getWeekTable().getDay(0).getLesson(7).getSubject()); + Assert.assertEquals("Uroczyste rozpoczęcie roku szkolnego 2015/2016", + excellent.getWeekTable().getDay(1).getLesson(1).getSubject()); + Assert.assertEquals("Geografia", + excellent.getWeekTable().getDay(3).getLesson(4).getSubject()); + + Assert.assertEquals("Naprawa komputera", + full.getWeekTable().getDay(1).getLesson(8).getSubject()); + Assert.assertEquals("Religia", + full.getWeekTable().getDay(3).getLesson(1).getSubject()); + Assert.assertEquals("Metodologia programowania", + full.getWeekTable().getDay(4).getLesson(5).getSubject()); + } + + @Test + public void getLessonIsNotExist() throws Exception { + Assert.assertTrue(excellent.getWeekTable().getDay(0).getLesson(5).isNotExist()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(1).isNotExist()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isNotExist()); + + Assert.assertFalse(full.getWeekTable().getDay(1).getLesson(12).isAbsenceUnexcused()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceUnexcused()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(5).isAbsenceUnexcused()); + } + + @Test + public void getLessonIsEmpty() throws Exception { + Assert.assertTrue(excellent.getWeekTable().getDay(0).getLesson(0).isEmpty()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(6).isEmpty()); + Assert.assertTrue(excellent.getWeekTable().getDay(4).getLesson(12).isEmpty()); + + Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(9).isEmpty()); + Assert.assertFalse(full.getWeekTable().getDay(2).getLesson(5).isEmpty()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(2).isEmpty()); + } + + @Test + public void getLessonIsPresence() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(7).isPresence()); + Assert.assertTrue(excellent.getWeekTable().getDay(1).getLesson(1).isPresence()); + Assert.assertTrue(excellent.getWeekTable().getDay(3).getLesson(7).isPresence()); + + Assert.assertTrue(full.getWeekTable().getDay(0).getLesson(1).isPresence()); + Assert.assertTrue(full.getWeekTable().getDay(2).getLesson(6).isPresence()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(7).isPresence()); + } + + @Test + public void getLessonIsAbsenceUnexcused() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(7).isAbsenceUnexcused()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(0).isAbsenceUnexcused()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(4).isAbsenceUnexcused()); + + Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(8).isAbsenceUnexcused()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceUnexcused()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isAbsenceUnexcused()); + } + + @Test + public void getLessonIsAbsenceExcused() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(7).isAbsenceExcused()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(0).isAbsenceExcused()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(4).isAbsenceExcused()); + + Assert.assertFalse(full.getWeekTable().getDay(2).getLesson(5).isAbsenceExcused()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceExcused()); + Assert.assertTrue(full.getWeekTable().getDay(4).getLesson(3).isAbsenceExcused()); + } + + @Test + public void getLessonIsAbsenceForSchoolReasons() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isAbsenceForSchoolReasons()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isAbsenceForSchoolReasons()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isAbsenceForSchoolReasons()); + + Assert.assertTrue(full.getWeekTable().getDay(2).getLesson(5).isAbsenceForSchoolReasons()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceForSchoolReasons()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isAbsenceForSchoolReasons()); + } + + @Test + public void getLessonIsUnexcusedLateness() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isUnexcusedLateness()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isUnexcusedLateness()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isUnexcusedLateness()); + + Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(6).isUnexcusedLateness()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isUnexcusedLateness()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isUnexcusedLateness()); + } + + @Test + public void getLessonIsExcusedLateness() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isExcusedLateness()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isExcusedLateness()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isExcusedLateness()); + + Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(7).isExcusedLateness()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isExcusedLateness()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isExcusedLateness()); + } + + @Test + public void getLessonIsExemption() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isExemption()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isExemption()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isExemption()); + + Assert.assertFalse(full.getWeekTable().getDay(2).getLesson(5).isExemption()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isExemption()); + Assert.assertTrue(full.getWeekTable().getDay(4).getLesson(8).isExemption()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java b/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java new file mode 100644 index 000000000..9b57ddd68 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java @@ -0,0 +1,97 @@ +package io.github.wulkanowy.api.exams; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class ExamsWeekTest extends StudentAndParentTestCase { + + private ExamsWeek onePerDay; + + @Before + public void getCurrent() throws Exception { + onePerDay = new ExamsWeek(getSnp("Sprawdziany-one-per-day.html")); + } + + @Test + public void getWeekTest() throws Exception { + Assert.assertEquals("23.10.2017", onePerDay.getCurrent().getStartDayDate()); + } + + @Test + public void getDaysListTest() throws Exception { + Assert.assertEquals(3, onePerDay.getCurrent().getDays().size()); + Assert.assertEquals(7, onePerDay.getWeek("", false).getDays().size()); + } + + @Test + public void getExamsListTest() throws Exception { + List notEmpty = onePerDay.getCurrent().getDays(); + Assert.assertEquals(1, notEmpty.get(0).getExamList().size()); + Assert.assertEquals(1, notEmpty.get(1).getExamList().size()); + Assert.assertEquals(1, notEmpty.get(2).getExamList().size()); + + List emptyToo = onePerDay.getWeek("", false).getDays(); + Assert.assertEquals(1, emptyToo.get(0).getExamList().size()); + Assert.assertEquals(1, emptyToo.get(1).getExamList().size()); + Assert.assertEquals(1, emptyToo.get(4).getExamList().size()); + } + + @Test + public void getDayDateTest() throws Exception { + List dayList = onePerDay.getCurrent().getDays(); + + Assert.assertEquals("23.10.2017", dayList.get(0).getDate()); + Assert.assertEquals("24.10.2017", dayList.get(1).getDate()); + Assert.assertEquals("27.10.2017", dayList.get(2).getDate()); + } + + @Test + public void getExamSubjectAndGroupTest() throws Exception { + List dayList = onePerDay.getCurrent().getDays(); + + Assert.assertEquals("Sieci komputerowe 3Ti|zaw2", dayList.get(0).getExamList().get(0).getSubjectAndGroup()); + Assert.assertEquals("Język angielski 3Ti|J1", dayList.get(1).getExamList().get(0).getSubjectAndGroup()); + Assert.assertEquals("Metodologia programowania 3Ti|zaw2", dayList.get(2).getExamList().get(0).getSubjectAndGroup()); + } + + @Test + public void getExamTypeTest() throws Exception { + List dayList = onePerDay.getCurrent().getDays(); + + Assert.assertEquals("Sprawdzian", dayList.get(0).getExamList().get(0).getType()); + Assert.assertEquals("Sprawdzian", dayList.get(1).getExamList().get(0).getType()); + Assert.assertEquals("Sprawdzian", dayList.get(2).getExamList().get(0).getType()); + } + + @Test + public void getExamDescriptionTest() throws Exception { + List dayList = onePerDay.getCurrent().getDays(); + + Assert.assertEquals("Łącza danych", dayList.get(0).getExamList().get(0).getDescription()); + Assert.assertEquals("Czasy teraźniejsze", dayList.get(1).getExamList().get(0).getDescription()); + Assert.assertEquals("", dayList.get(2).getExamList().get(0).getDescription()); + } + + @Test + public void getExamTeacherTest() throws Exception { + List dayList = onePerDay.getCurrent().getDays(); + + Assert.assertEquals("Adam Wiśniewski [AW]", dayList.get(0).getExamList().get(0).getTeacher()); + Assert.assertEquals("Natalia Nowak [NN]", dayList.get(1).getExamList().get(0).getTeacher()); + Assert.assertEquals("Małgorzata Nowacka [MN]", dayList.get(2).getExamList().get(0).getTeacher()); + } + + @Test + public void getExamEntryDateTest() throws Exception { + List dayList = onePerDay.getCurrent().getDays(); + + Assert.assertEquals("16.10.2017", dayList.get(0).getExamList().get(0).getEntryDate()); + Assert.assertEquals("17.10.2017", dayList.get(1).getExamList().get(0).getEntryDate()); + Assert.assertEquals("16.10.2017", dayList.get(2).getExamList().get(0).getEntryDate()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java b/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java new file mode 100644 index 000000000..bee5b4fee --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java @@ -0,0 +1,114 @@ +package io.github.wulkanowy.api.grades; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class GradesListTest extends StudentAndParentTestCase { + + private GradesList filled; + + @Before + public void setUp() throws Exception { + filled = new GradesList(getSnp("OcenyWszystkie-filled.html")); + } + + @Test + public void getAllTest() throws Exception { + Assert.assertEquals(6, filled.getAll().size()); // 2 items are skipped + } + + @Test + public void getSubjectTest() throws Exception { + List list = filled.getAll(); + + Assert.assertEquals("Zajęcia z wychowawcą", list.get(0).getSubject()); + Assert.assertEquals("Język angielski", list.get(3).getSubject()); + Assert.assertEquals("Wychowanie fizyczne", list.get(4).getSubject()); + Assert.assertEquals("Język polski", list.get(5).getSubject()); + } + + @Test + public void getValueTest() throws Exception { + List list = filled.getAll(); + + Assert.assertEquals("5", list.get(0).getValue()); + Assert.assertEquals("5", list.get(3).getValue()); + Assert.assertEquals("1", list.get(4).getValue()); + Assert.assertEquals("1", list.get(5).getValue()); + } + + @Test + public void getColorTest() throws Exception { + List list = filled.getAll(); + + Assert.assertEquals("000000", list.get(0).getColor()); + Assert.assertEquals("1289F7", list.get(3).getColor()); + Assert.assertEquals("6ECD07", list.get(4).getColor()); + Assert.assertEquals("6ECD07", list.get(5).getColor()); + } + + @Test + public void getSymbolTest() throws Exception { + List list = filled.getAll(); + + Assert.assertEquals("A1", list.get(0).getSymbol()); + Assert.assertEquals("BW3", list.get(3).getSymbol()); + Assert.assertEquals("STR", list.get(4).getSymbol()); + Assert.assertEquals("K", list.get(5).getSymbol()); + } + + @Test + public void getDescriptionTest() throws Exception { + List list = filled.getAll(); + + Assert.assertEquals("Dzień Kobiet w naszej klasie", list.get(0).getDescription()); + Assert.assertEquals("Writing", list.get(3).getDescription()); + Assert.assertEquals("", list.get(4).getDescription()); + Assert.assertEquals("Kordian", list.get(5).getDescription()); + } + + @Test + public void getWeightTest() throws Exception { + List list = filled.getAll(); + + Assert.assertEquals("1,00", list.get(0).getWeight()); + Assert.assertEquals("3,00", list.get(3).getWeight()); + Assert.assertEquals("8,00", list.get(4).getWeight()); + Assert.assertEquals("5,00", list.get(5).getWeight()); + } + + @Test + public void getDateTest() throws Exception { + List list = filled.getAll(); + + Assert.assertEquals("2017-03-21", list.get(0).getDate()); + Assert.assertEquals("2017-06-02", list.get(3).getDate()); + Assert.assertEquals("2017-04-02", list.get(4).getDate()); + Assert.assertEquals("2017-02-06", list.get(5).getDate()); + } + + @Test + public void getTeacherTest() throws Exception { + List list = filled.getAll(); + + Assert.assertEquals("Patryk Maciejewski", list.get(0).getTeacher()); + Assert.assertEquals("Oliwia Woźniak", list.get(3).getTeacher()); + Assert.assertEquals("Klaudia Dziedzic", list.get(4).getTeacher()); + Assert.assertEquals("Amelia Stępień", list.get(5).getTeacher()); + } + + @Test + public void getSemesterTest() throws Exception { + List list = filled.getAll(); + + Assert.assertEquals("7654321", list.get(0).getSemester()); + Assert.assertEquals("7654321", list.get(3).getSemester()); + Assert.assertEquals("7654321", list.get(4).getSemester()); + Assert.assertEquals("7654321", list.get(5).getSemester()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/grades/SubjectsListTest.java b/api/src/test/java/io/github/wulkanowy/api/grades/SubjectsListTest.java new file mode 100644 index 000000000..d32283781 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/grades/SubjectsListTest.java @@ -0,0 +1,82 @@ +package io.github.wulkanowy.api.grades; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class SubjectsListTest extends StudentAndParentTestCase { + + private SubjectsList std; + + private SubjectsList average; + + @Before + public void setUp() throws Exception { + std = new SubjectsList(getSnp("OcenyWszystkie-subjects.html")); + average = new SubjectsList(getSnp("OcenyWszystkie-subjects-average.html")); + } + + @Test + public void getAllTest() throws Exception { + Assert.assertEquals(5, std.getAll().size()); + Assert.assertEquals(5, average.getAll().size()); + } + + @Test + public void getNameTest() throws Exception { + List stdList = std.getAll(); + + Assert.assertEquals("Zachowanie", stdList.get(0).getName()); + Assert.assertEquals("Praktyka zawodowa", stdList.get(1).getName()); + Assert.assertEquals("Metodologia programowania", stdList.get(2).getName()); + Assert.assertEquals("Podstawy przedsiębiorczości", stdList.get(3).getName()); + Assert.assertEquals("Wychowanie do życia w rodzinie", stdList.get(4).getName()); + + List averageList = average.getAll(); + Assert.assertEquals("Zachowanie", averageList.get(0).getName()); + Assert.assertEquals("Język polski", averageList.get(1).getName()); + Assert.assertEquals("Wychowanie fizyczne", averageList.get(2).getName()); + Assert.assertEquals("Język angielski", averageList.get(3).getName()); + Assert.assertEquals("Wiedza o społeczeństwie", averageList.get(4).getName()); + } + + @Test + public void getPredictedRatingTest() throws Exception { + List stdList = std.getAll(); + + Assert.assertEquals("bardzo dobre", stdList.get(0).getPredictedRating()); + Assert.assertEquals("-", stdList.get(1).getPredictedRating()); + Assert.assertEquals("bardzo dobry", stdList.get(2).getPredictedRating()); + Assert.assertEquals("3/4", stdList.get(3).getPredictedRating()); + Assert.assertEquals("-", stdList.get(4).getPredictedRating()); + + List averageList = average.getAll(); + Assert.assertEquals("bardzo dobre", averageList.get(0).getPredictedRating()); + Assert.assertEquals("-", averageList.get(1).getPredictedRating()); + Assert.assertEquals("bardzo dobry", averageList.get(2).getPredictedRating()); + Assert.assertEquals("4/5", averageList.get(3).getPredictedRating()); + Assert.assertEquals("-", averageList.get(4).getPredictedRating()); + } + + @Test + public void getFinalRatingTest() throws Exception { + List stdList = std.getAll(); + + Assert.assertEquals("bardzo dobre", stdList.get(0).getFinalRating()); + Assert.assertEquals("celujący", stdList.get(1).getFinalRating()); + Assert.assertEquals("celujący", stdList.get(2).getFinalRating()); + Assert.assertEquals("dostateczny", stdList.get(3).getFinalRating()); + Assert.assertEquals("-", stdList.get(4).getFinalRating()); + + List averageList = average.getAll(); + Assert.assertEquals("bardzo dobre", averageList.get(0).getFinalRating()); + Assert.assertEquals("dobry", averageList.get(1).getFinalRating()); + Assert.assertEquals("celujący", averageList.get(2).getFinalRating()); + Assert.assertEquals("bardzo dobry", averageList.get(3).getFinalRating()); + Assert.assertEquals("-", averageList.get(4).getFinalRating()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java b/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java new file mode 100644 index 000000000..69118018b --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java @@ -0,0 +1,96 @@ +package io.github.wulkanowy.api.login; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import io.github.wulkanowy.api.Client; +import io.github.wulkanowy.api.FixtureHelper; + +public class LoginTest { + + private String getFixtureAsString(String fixtureFileName) { + return FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName)); + } + + private Client getClient(String fixtureFileName) throws Exception { + Document doc = Jsoup.parse(getFixtureAsString(fixtureFileName)); + + Client client = Mockito.mock(Client.class); + Mockito.when(client.postPageByUrl(Mockito.anyString(), Mockito.any(String[][].class))).thenReturn(doc); + + return client; + } + + @Test + public void loginTest() throws Exception { + Login login = new Login(getClient("Logowanie-success.html")); + + Assert.assertEquals("d123", login.login("a@a", "pswd", "d123")); + } + + @Test(expected = BadCredentialsException.class) + public void sendWrongCredentialsTest() throws Exception { + Login login = new Login(getClient("Logowanie-error.html")); + + login.sendCredentials("a@a", "pswd", "d123"); + } + + @Test + public void sendCredentialsCertificateTest() throws Exception { + Login login = new Login(getClient("Logowanie-certyfikat.html")); + + Assert.assertEquals( + getFixtureAsString("cert.xml").replaceAll("\\s+",""), + login.sendCredentials("a@a", "passwd", "d123").replaceAll("\\s+","") + ); + } + + @Test + public void sendCertificateNotDefaultSymbolSuccessTest() throws Exception { + Login login = new Login(getClient("Logowanie-success.html")); + + Assert.assertEquals("wulkanowyschool321", + login.sendCertificate("", "wulkanowyschool321")); + } + + @Test + public void sendCertificateDefaultSymbolSuccessTest() throws Exception { + Login login = new Login(getClient("Logowanie-success.html")); + + Assert.assertEquals("demo12345", + login.sendCertificate(getFixtureAsString("cert.xml"), "Default")); + } + + @Test(expected = AccountPermissionException.class) + public void sendCertificateAccountPermissionTest() throws Exception { + Login login = new Login(getClient("Logowanie-brak-dostepu.html")); + + login.sendCertificate(getFixtureAsString("cert.xml"), "demo123"); + } + + @Test(expected = LoginErrorException.class) + public void sendCertificateLoginErrorTest() throws Exception { + Login login = new Login(getClient("Logowanie-certyfikat.html")); // change to other document + + login.sendCertificate(getFixtureAsString("cert.xml"), "demo123"); + } + + @Test + public void findSymbolInCertificateTest() throws Exception { + Login login = new Login(getClient("Logowanie-certyfikat.html")); + + String certificate = getFixtureAsString("cert.xml"); + + Assert.assertEquals("demo12345", login.findSymbolInCertificate(certificate)); + } + + @Test + public void findSymbolInInvalidCertificateTest() throws Exception { + Login login = new Login(getClient("Logowanie-certyfikat.html")); + + Assert.assertEquals("", login.findSymbolInCertificate("")); // change to real cert with empty symbols + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/messages/MessagesTest.java b/api/src/test/java/io/github/wulkanowy/api/messages/MessagesTest.java new file mode 100644 index 000000000..14fa4627e --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/messages/MessagesTest.java @@ -0,0 +1,89 @@ +package io.github.wulkanowy.api.messages; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.List; + +import io.github.wulkanowy.api.Client; +import io.github.wulkanowy.api.FixtureHelper; +import io.github.wulkanowy.api.NotLoggedInErrorException; + +public class MessagesTest { + + private Client getFixtureAsString(String fixtureFileName) throws Exception { + Client client = Mockito.mock(Client.class); + Mockito.when(client.getJsonStringByUrl(Mockito.anyString())) + .thenReturn(FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName))); + Mockito.when(client.postJsonStringByUrl(Mockito.anyString(), Mockito.any())) + .thenReturn(FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName))); + return client; + } + + @Test + public void getMessages() throws Exception { + Client client = getFixtureAsString("GetWiadomosciOdebrane.json"); + + Messages messages = new Messages(client); + List messageList = messages.getReceived(); + + Assert.assertEquals(true, messageList.get(1).unread); + Assert.assertEquals("2016-03-15 09:00:00", messageList.get(0).date); + Assert.assertEquals(null, messageList.get(0).content); + Assert.assertEquals("Kowalski Jan", messageList.get(0).sender); + Assert.assertEquals(12347, messageList.get(2).id); + } + + @Test + public void getMessagesEmpty() throws Exception { + Client client = getFixtureAsString("GetWiadomosciUsuniete-empty.json"); + + Messages messages = new Messages(client); + List messageList = messages.getSent(); + + Assert.assertTrue(messageList.isEmpty()); + } + + @Test(expected = NotLoggedInErrorException.class) + public void getMessagesError() throws Exception { + Client client = getFixtureAsString("UndefinedError.txt"); + + Messages messages = new Messages(client); + messages.getDeleted(); + } + + @Test(expected = BadRequestException.class) + public void getMessagesBadRequest() throws Exception { + Client client = getFixtureAsString("PageError.html"); + + Messages messages = new Messages(client); + messages.getDeleted(); + } + + @Test + public void getMessage() throws Exception { + Client client = getFixtureAsString("GetTrescWiadomosci.json"); + + Messages messages = new Messages(client); + Message message = messages.getMessage(123, Messages.RECEIVED_FOLDER); + Assert.assertEquals(12345, message.id); + Assert.assertEquals("Witam, …. \nPozdrawiam Krzysztof Czerkas", message.content); + } + + @Test(expected = NotLoggedInErrorException.class) + public void getMessageError() throws Exception { + Client client = getFixtureAsString("UndefinedError.txt"); + + Messages messages = new Messages(client); + messages.getMessage(321, Messages.SENT_FOLDER); + } + + @Test(expected = BadRequestException.class) + public void getMessageBadRequest() throws Exception { + Client client = getFixtureAsString("PageError.html"); + + Messages messages = new Messages(client); + messages.getMessage(1, Messages.DELETED_FOLDER); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/notes/AchievementsListTest.java b/api/src/test/java/io/github/wulkanowy/api/notes/AchievementsListTest.java new file mode 100644 index 000000000..0d7774bb4 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/notes/AchievementsListTest.java @@ -0,0 +1,36 @@ +package io.github.wulkanowy.api.notes; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class AchievementsListTest extends StudentAndParentTestCase { + + private AchievementsList filledAchievementsList; + + private AchievementsList emptyAchievementsList; + + @Before + public void setUp() throws Exception { + filledAchievementsList = new AchievementsList(getSnp("UwagiOsiagniecia-filled.html")); + emptyAchievementsList = new AchievementsList(getSnp("UwagiOsiagniecia-empty.html")); + } + + @Test + public void getAllAchievementsTest() throws Exception { + Assert.assertEquals(2, filledAchievementsList.getAllAchievements().size()); + Assert.assertEquals(0, emptyAchievementsList.getAllAchievements().size()); + } + + @Test + public void getAchievements() throws Exception { + List filledList = filledAchievementsList.getAllAchievements(); + + Assert.assertEquals("I miejsce w ogólnopolskim konkursie ortograficznym", filledList.get(0)); + Assert.assertEquals("III miejsce w ogólnopolskim konkursie plastycznym", filledList.get(1)); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java b/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java new file mode 100644 index 000000000..d76c06486 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java @@ -0,0 +1,60 @@ +package io.github.wulkanowy.api.notes; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class NotesListTest extends StudentAndParentTestCase { + + private NotesList filled; + + private NotesList empty; + + @Before + public void setUp() throws Exception { + filled = new NotesList(getSnp("UwagiOsiagniecia-filled.html")); + empty = new NotesList(getSnp("UwagiOsiagniecia-empty.html")); + } + + @Test + public void getAllNotesTest() throws Exception { + Assert.assertEquals(3, filled.getAllNotes().size()); + Assert.assertEquals(0, empty.getAllNotes().size()); + } + + @Test + public void getDateTest() throws Exception { + List filledList = filled.getAllNotes(); + + Assert.assertEquals("06.06.2017", filledList.get(0).getDate()); + Assert.assertEquals("01.10.2016", filledList.get(2).getDate()); + } + + @Test + public void getTeacherTest() throws Exception { + List filledList = filled.getAllNotes(); + + Assert.assertEquals("Jan Kowalski [JK]", filledList.get(0).getTeacher()); + Assert.assertEquals("Kochański Leszek [KL]", filledList.get(2).getTeacher()); + } + + @Test + public void getCategoryTest() throws Exception { + List filledList = filled.getAllNotes(); + + Assert.assertEquals("Zaangażowanie społeczne", filledList.get(0).getCategory()); + Assert.assertEquals("Zachowanie na lekcji", filledList.get(2).getCategory()); + } + + @Test + public void getContentTest() throws Exception { + List filledList = filled.getAllNotes(); + + Assert.assertEquals("Pomoc przy pikniku charytatywnym", filledList.get(0).getContent()); + Assert.assertEquals("Przeszkadzanie w prowadzeniu lekcji", filledList.get(2).getContent()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/school/SchoolInfoTest.java b/api/src/test/java/io/github/wulkanowy/api/school/SchoolInfoTest.java new file mode 100644 index 000000000..947db9b8f --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/school/SchoolInfoTest.java @@ -0,0 +1,48 @@ +package io.github.wulkanowy.api.school; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class SchoolInfoTest extends StudentAndParentTestCase { + + private SchoolInfo schoolInfo; + + @Before + public void setUp() throws Exception { + schoolInfo = new SchoolInfo(getSnp("Szkola.html")); + } + + @Test + public void getNameTest() throws Exception { + Assert.assertEquals("Zespół Szkół nr 64", schoolInfo.getSchoolData().getName()); + } + + @Test + public void getAddressTest() throws Exception { + Assert.assertEquals("ul. Wiśniowa 128, 01-234 Rogalowo, Nibylandia", + schoolInfo.getSchoolData().getAddress()); + } + + @Test + public void getPhoneNumberTest() throws Exception { + Assert.assertEquals("55 5555555", schoolInfo.getSchoolData().getPhoneNumber()); + } + + @Test + public void getHeadmasterTest() throws Exception { + Assert.assertEquals("Antoni Sobczyk", schoolInfo.getSchoolData().getHeadmaster()); + } + + @Test + public void getPedagoguesTest() throws Exception { + Assert.assertArrayEquals(new String[]{ + "Zofia Czerwińska [ZC]", + "Aleksander Krzemiński [AK]", + "Karolina Kowalska [KK]", + "Bartek Dąbrowski [BD]" + }, schoolInfo.getSchoolData().getPedagogues()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/school/TeachersInfoTest.java b/api/src/test/java/io/github/wulkanowy/api/school/TeachersInfoTest.java new file mode 100644 index 000000000..b0da356b1 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/school/TeachersInfoTest.java @@ -0,0 +1,56 @@ +package io.github.wulkanowy.api.school; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class TeachersInfoTest extends StudentAndParentTestCase { + + private TeachersInfo teachersInfo; + + @Before + public void setUp() throws Exception { + teachersInfo = new TeachersInfo(getSnp("Szkola.html")); + } + + @Test + public void getClassNameTest() throws Exception { + Assert.assertEquals("1a", teachersInfo.getTeachersData().getClassName()); + } + + @Test + public void getClassTeacherTest() throws Exception { + Assert.assertArrayEquals(new String[]{ + "Karolina Kowalska [AN]", + "Antoni Sobczyk [AS]" + }, teachersInfo.getTeachersData().getClassTeacher()); + } + + @Test + public void getTeachersDataSubjectsNameTest() throws Exception { + List subjects = teachersInfo.getTeachersData().getSubjects(); + + Assert.assertEquals("Biologia", subjects.get(0).getName()); + Assert.assertEquals("Język angielski", subjects.get(6).getName()); + } + + @Test + public void getTeachersDataSubjectsTeachersTest() throws Exception { + List subjects = teachersInfo.getTeachersData().getSubjects(); + + Assert.assertArrayEquals(new String[]{"Karolina Kowalska [AN]"}, + subjects.get(0).getTeachers()); + Assert.assertEquals("Karolina Kowalska [AN]", + subjects.get(0).getTeachers()[0]); + + Assert.assertArrayEquals(new String[]{ + "Karolina Kowalska [AN]", + "Mateusz Kowal [MK]", + "Amelia Mazur [AM]" + }, subjects.get(6).getTeachers()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java b/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java new file mode 100644 index 000000000..652676348 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java @@ -0,0 +1,242 @@ +package io.github.wulkanowy.api.timetable; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class TimetableTest extends StudentAndParentTestCase { + + private Timetable std; + + private Timetable full; + + private Timetable holidays; + + @Before + public void setUp() throws Exception { + std = new Timetable(getSnp("PlanLekcji-std.html")); + full = new Timetable(getSnp("PlanLekcji-full.html")); + holidays = new Timetable(getSnp("PlanLekcji-holidays.html")); + } + + // Week + + @Test + public void getWeekTableTest() throws Exception { + Assert.assertEquals(5, std.getWeekTable().getDays().size()); + Assert.assertEquals(5, full.getWeekTable().getDays().size()); + Assert.assertEquals(5, holidays.getWeekTable().getDays().size()); + } + + @Test + public void getStartDayDateTest() throws Exception { + Assert.assertEquals("2017-06-19", std.getWeekTable().getStartDayDate()); + Assert.assertEquals("2017-06-19", full.getWeekTable().getStartDayDate()); + Assert.assertEquals("2017-07-31", holidays.getWeekTable().getStartDayDate()); + } + + // ExamDay + + @Test + public void getDayNameTest() throws Exception { + Assert.assertEquals("poniedziałek", std.getWeekTable().getDay(0).getDayName()); + Assert.assertEquals("piątek", std.getWeekTable().getDay(4).getDayName()); + Assert.assertEquals("wtorek", full.getWeekTable().getDay(1).getDayName()); + Assert.assertEquals("czwartek", full.getWeekTable().getDay(3).getDayName()); + Assert.assertEquals("środa", holidays.getWeekTable().getDay(2).getDayName()); + } + + @Test + public void getDayDateTest() throws Exception { + Assert.assertEquals("2017-06-19", std.getWeekTable().getDay(0).getDate()); + Assert.assertEquals("2017-06-23", std.getWeekTable().getDay(4).getDate()); + Assert.assertEquals("2017-06-20", full.getWeekTable().getDay(1).getDate()); + Assert.assertEquals("2017-06-22", full.getWeekTable().getDay(3).getDate()); + Assert.assertEquals("2017-08-02", holidays.getWeekTable().getDay(2).getDate()); + } + + @Test + public void getDayIsFreeTest() throws Exception { + Assert.assertFalse(std.getWeekTable().getDay(0).isFreeDay()); + Assert.assertFalse(full.getWeekTable().getDay(2).isFreeDay()); + Assert.assertTrue(holidays.getWeekTable().getDay(4).isFreeDay()); + } + + @Test + public void getDayFreeDayName() throws Exception { + Assert.assertNotEquals("Wakacje", std.getWeekTable().getDay(0).getFreeDayName()); + Assert.assertNotEquals("Ferie letnie", full.getWeekTable().getDay(1).getFreeDayName()); + Assert.assertNotEquals("Wakacje", holidays.getWeekTable().getDay(2).getFreeDayName()); + Assert.assertEquals("Ferie letnie", holidays.getWeekTable().getDay(4).getFreeDayName()); + } + + @Test + public void getDayLessonsTest() throws Exception { + Assert.assertEquals(8, std.getWeekTable().getDay(0).getLessons().size()); + Assert.assertEquals(14, full.getWeekTable().getDay(2).getLessons().size()); + Assert.assertEquals(14, holidays.getWeekTable().getDay(4).getLessons().size()); + } + + // Lesson + + @Test + public void getLessonNumberTest() throws Exception { + Assert.assertEquals("2", std.getWeekTable().getDay(0).getLesson(1).getNumber()); + Assert.assertEquals("5", std.getWeekTable().getDay(2).getLesson(4).getNumber()); + Assert.assertEquals("0", full.getWeekTable().getDay(0).getLesson(0).getNumber()); + Assert.assertEquals("13", full.getWeekTable().getDay(4).getLesson(13).getNumber()); + Assert.assertEquals("3", holidays.getWeekTable().getDay(3).getLesson(3).getNumber()); + } + + @Test + public void getLessonDayTest() throws Exception { + Assert.assertEquals("2017-06-19", std.getWeekTable().getDay(0).getLesson(1).getDate()); + Assert.assertEquals("2017-06-23", std.getWeekTable().getDay(4).getLesson(4).getDate()); + Assert.assertEquals("2017-06-20", full.getWeekTable().getDay(1).getLesson(6).getDate()); + Assert.assertEquals("2017-06-22", full.getWeekTable().getDay(3).getLesson(3).getDate()); + Assert.assertEquals("2017-08-02", holidays.getWeekTable().getDay(2).getLesson(8).getDate()); + } + + @Test + public void getLessonSubjectTest() throws Exception { + Assert.assertEquals("Historia", std.getWeekTable().getDay(0).getLesson(1).getSubject()); + Assert.assertEquals("Zajęcia techniczne", std.getWeekTable().getDay(2).getLesson(4).getSubject()); + Assert.assertEquals("Wychowanie fizyczne", std.getWeekTable().getDay(1).getLesson(1).getSubject()); + Assert.assertEquals("Język angielski", full.getWeekTable().getDay(0).getLesson(1).getSubject()); + Assert.assertEquals("Wychowanie do życia w rodzinie", full.getWeekTable().getDay(2).getLesson(0).getSubject()); + Assert.assertEquals("Wychowanie fizyczne", full.getWeekTable().getDay(3).getLesson(1).getSubject()); + Assert.assertEquals("Uroczyste zakończenie roku szkolnego", full.getWeekTable().getDay(4).getLesson(0).getSubject()); + Assert.assertEquals("Fizyka", full.getWeekTable().getDay(0).getLesson(0).getSubject()); + Assert.assertEquals("Metodologia programowania", full.getWeekTable().getDay(1).getLesson(0).getSubject()); + Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getSubject()); + } + + @Test + public void getLessonTeacherTest() throws Exception { + Assert.assertEquals("Bogatka Katarzyna", std.getWeekTable().getDay(0).getLesson(1).getTeacher()); + Assert.assertEquals("Chlebowski Stanisław", std.getWeekTable().getDay(2).getLesson(4).getTeacher()); + Assert.assertEquals("Kobczyk Iwona", full.getWeekTable().getDay(0).getLesson(1).getTeacher()); + Assert.assertEquals("Bączek Grzegorz", full.getWeekTable().getDay(0).getLesson(7).getTeacher()); + Assert.assertEquals("Nowak Jadwiga", full.getWeekTable().getDay(2).getLesson(0).getTeacher()); + Assert.assertEquals("Nowicka Irena", full.getWeekTable().getDay(3).getLesson(1).getTeacher()); + Assert.assertEquals("Baran Małgorzata", full.getWeekTable().getDay(4).getLesson(0).getTeacher()); + Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getTeacher()); + } + + @Test + public void getLessonRoomTest() throws Exception { + Assert.assertEquals("", std.getWeekTable().getDay(3).getLesson(3).getRoom()); + Assert.assertEquals("33", full.getWeekTable().getDay(0).getLesson(7).getRoom()); + Assert.assertEquals("19", full.getWeekTable().getDay(0).getLesson(0).getRoom()); + Assert.assertEquals("32", full.getWeekTable().getDay(1).getLesson(0).getRoom()); + Assert.assertEquals("32", full.getWeekTable().getDay(1).getLesson(8).getRoom()); + Assert.assertEquals("32", full.getWeekTable().getDay(2).getLesson(8).getRoom()); + Assert.assertEquals("G4", full.getWeekTable().getDay(3).getLesson(1).getRoom()); + Assert.assertEquals("37", full.getWeekTable().getDay(4).getLesson(0).getRoom()); + Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getRoom()); + } + + @Test + public void getLessonDescriptionTest() throws Exception { + Assert.assertEquals("", std.getWeekTable().getDay(3).getLesson(3).getDescription()); + Assert.assertEquals("przeniesiona z lekcji 7, 01.12.2017", full.getWeekTable().getDay(1).getLesson(1).getDescription()); + Assert.assertEquals("okienko dla uczniów", full.getWeekTable().getDay(0).getLesson(7).getDescription()); + Assert.assertEquals("przeniesiona z lekcji 7, 20.06.2017", full.getWeekTable().getDay(1).getLesson(2).getDescription()); + Assert.assertEquals("przeniesiona z lekcji 4, 20.06.2017", full.getWeekTable().getDay(1).getLesson(3).getDescription()); + Assert.assertEquals("zastępstwo (poprzednio: Religia)", full.getWeekTable().getDay(2).getLesson(0).getDescription()); + Assert.assertEquals("zastępstwo (poprzednio: Wychowanie fizyczne)", full.getWeekTable().getDay(3).getLesson(1).getDescription()); + Assert.assertEquals("", full.getWeekTable().getDay(4).getLesson(0).getDescription()); + Assert.assertEquals("egzamin", full.getWeekTable().getDay(3).getLesson(0).getDescription()); + Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getDescription()); + } + + @Test + public void getLessonGroupNameTest() throws Exception { + Assert.assertEquals("CH", std.getWeekTable().getDay(0).getLesson(2).getGroupName()); + Assert.assertEquals("JNPW", std.getWeekTable().getDay(4).getLesson(0).getGroupName()); + Assert.assertEquals("", full.getWeekTable().getDay(0).getLesson(7).getGroupName()); + Assert.assertEquals("zaw2", full.getWeekTable().getDay(1).getLesson(0).getGroupName()); + Assert.assertEquals("wf2", full.getWeekTable().getDay(1).getLesson(3).getGroupName()); + Assert.assertEquals("zaw1", full.getWeekTable().getDay(3).getLesson(1).getGroupName()); + Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getGroupName()); + } + + @Test + public void getLessonStartTimeTest() throws Exception { + Assert.assertEquals("08:00", std.getWeekTable().getDay(0).getLesson(0).getStartTime()); + Assert.assertEquals("14:10", std.getWeekTable().getDay(3).getLesson(7).getStartTime()); + Assert.assertEquals("07:10", full.getWeekTable().getDay(0).getLesson(0).getStartTime()); + Assert.assertEquals("12:20", full.getWeekTable().getDay(2).getLesson(6).getStartTime()); + Assert.assertEquals("12:20", holidays.getWeekTable().getDay(2).getLesson(6).getStartTime()); + } + + @Test + public void getLessonEndTimeTest() throws Exception { + Assert.assertEquals("08:45", std.getWeekTable().getDay(1).getLesson(0).getEndTime()); + Assert.assertEquals("12:15", std.getWeekTable().getDay(2).getLesson(4).getEndTime()); + Assert.assertEquals("07:55", full.getWeekTable().getDay(1).getLesson(0).getEndTime()); + Assert.assertEquals("19:00", full.getWeekTable().getDay(3).getLesson(13).getEndTime()); + Assert.assertEquals("19:00", holidays.getWeekTable().getDay(3).getLesson(13).getEndTime()); + } + + @Test + public void getLessonIsEmptyTest() throws Exception { + Assert.assertFalse(std.getWeekTable().getDay(1).getLesson(4).isEmpty()); + Assert.assertTrue(std.getWeekTable().getDay(3).getLesson(7).isEmpty()); + Assert.assertFalse(full.getWeekTable().getDay(1).getLesson(1).isEmpty()); + Assert.assertFalse(full.getWeekTable().getDay(1).getLesson(2).isEmpty()); + Assert.assertFalse(full.getWeekTable().getDay(0).getLesson(7).isEmpty()); + Assert.assertTrue(full.getWeekTable().getDay(2).getLesson(9).isEmpty()); + Assert.assertTrue(holidays.getWeekTable().getDay(0).getLesson(5).isEmpty()); + Assert.assertTrue(holidays.getWeekTable().getDay(4).getLesson(13).isEmpty()); + } + + @Test + public void getLessonIsDivisionIntoGroupsTest() throws Exception { + Assert.assertTrue(std.getWeekTable().getDay(0).getLesson(2).isDivisionIntoGroups()); + Assert.assertTrue(std.getWeekTable().getDay(4).getLesson(0).isDivisionIntoGroups()); + Assert.assertFalse(full.getWeekTable().getDay(0).getLesson(7).isDivisionIntoGroups()); + Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(3).isDivisionIntoGroups()); + Assert.assertTrue(full.getWeekTable().getDay(3).getLesson(1).isDivisionIntoGroups()); + Assert.assertFalse(holidays.getWeekTable().getDay(3).getLesson(3).isDivisionIntoGroups()); + } + + @Test + public void getLessonIsPlanningTest() throws Exception { + Assert.assertFalse(std.getWeekTable().getDay(4).getLesson(4).isPlanning()); + Assert.assertFalse(full.getWeekTable().getDay(0).getLesson(1).isPlanning()); + Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(3).isPlanning()); + Assert.assertTrue(full.getWeekTable().getDay(4).getLesson(0).isPlanning()); + Assert.assertFalse(holidays.getWeekTable().getDay(3).getLesson(3).isPlanning()); + } + + @Test + public void getLessonIsRealizedTest() throws Exception { + Assert.assertTrue(std.getWeekTable().getDay(3).getLesson(3).isRealized()); + Assert.assertTrue(full.getWeekTable().getDay(0).getLesson(1).isRealized()); + Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(3).isRealized()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(0).isRealized()); + Assert.assertFalse(holidays.getWeekTable().getDay(3).getLesson(3).isRealized()); + } + + @Test + public void getLessonIsMovedOrCanceledTest() throws Exception { + Assert.assertFalse(std.getWeekTable().getDay(3).getLesson(3).isMovedOrCanceled()); + Assert.assertTrue(full.getWeekTable().getDay(0).getLesson(7).isMovedOrCanceled()); + Assert.assertFalse(full.getWeekTable().getDay(1).getLesson(3).isMovedOrCanceled()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(0).isMovedOrCanceled()); + Assert.assertFalse(holidays.getWeekTable().getDay(3).getLesson(3).isMovedOrCanceled()); + } + + @Test + public void getLessonIsNewMovedInOrChangedTest() throws Exception { + Assert.assertFalse(std.getWeekTable().getDay(3).getLesson(3).isNewMovedInOrChanged()); + Assert.assertFalse(full.getWeekTable().getDay(0).getLesson(1).isNewMovedInOrChanged()); + Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(2).isNewMovedInOrChanged()); + Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(3).isNewMovedInOrChanged()); + Assert.assertTrue(full.getWeekTable().getDay(3).getLesson(1).isNewMovedInOrChanged()); + Assert.assertFalse(holidays.getWeekTable().getDay(3).getLesson(3).isNewMovedInOrChanged()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/user/BasicInformationTest.java b/api/src/test/java/io/github/wulkanowy/api/user/BasicInformationTest.java new file mode 100644 index 000000000..6b729c95a --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/user/BasicInformationTest.java @@ -0,0 +1,106 @@ +package io.github.wulkanowy.api.user; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class BasicInformationTest extends StudentAndParentTestCase { + + private BasicInformation basicInformation; + + @Before + public void setUp() throws Exception { + basicInformation = new BasicInformation(getSnp("UczenDanePodstawowe.html")); + } + + @Test + public void getPersonalFirstNameTest() throws Exception { + Assert.assertEquals("Maria", basicInformation.getPersonalData().getFirstName()); + } + + @Test + public void getPersonalSurnameTest() throws Exception { + Assert.assertEquals("Kamińska", basicInformation.getPersonalData().getSurname()); + } + + @Test + public void getPersonalFirstAndLastNameTest() throws Exception { + Assert.assertEquals("Maria Kamińska", + basicInformation.getPersonalData().getFirstAndLastName()); + } + + @Test + public void getPersonalNameTest() throws Exception { + Assert.assertEquals("Maria Aneta Kamińska", basicInformation.getPersonalData().getName()); + } + + @Test + public void getPersonalDateAndBirthPlaceTest() throws Exception { + Assert.assertEquals("01.01.1900, Warszawa", + basicInformation.getPersonalData().getDateAndBirthPlace()); + } + + @Test + public void getPersonalPeselTest() throws Exception { + Assert.assertEquals("12345678900", basicInformation.getPersonalData().getPesel()); + } + + @Test + public void getPersonalGenderTest() throws Exception { + Assert.assertEquals("Kobieta", basicInformation.getPersonalData().getGender()); + } + + @Test + public void isPersonalPolishCitizenshipTest() throws Exception { + Assert.assertTrue(basicInformation.getPersonalData().isPolishCitizenship()); + } + + @Test + public void getPersonalFamilyNameTest() throws Exception { + Assert.assertEquals("Nowak", basicInformation.getPersonalData().getFamilyName()); + } + + @Test + public void getPersonalParentsNames() throws Exception { + Assert.assertEquals("Gabriela, Kamil", + basicInformation.getPersonalData().getParentsNames()); + } + + @Test + public void getBasicAddressTest() throws Exception { + Assert.assertEquals("ul. Sportowa 16, 00-123 Warszawa", + basicInformation.getAddressData().getAddress()); + } + + @Test + public void getBasicRegisteredAddressTest() throws Exception { + Assert.assertEquals("ul. Sportowa 17, 00-123 Warszawa", + basicInformation.getAddressData().getRegisteredAddress()); + } + + @Test + public void getBasicCorrespondenceAddressTest() throws Exception { + Assert.assertEquals("ul. Sportowa 18, 00-123 Warszawa", + basicInformation.getAddressData().getCorrespondenceAddress()); + } + + @Test + public void getContactPhoneNumberTest() throws Exception { + Assert.assertEquals("005554433", + basicInformation.getContactDetails().getPhoneNumber()); + } + + @Test + public void getContactCellPhoneNumberTest() throws Exception { + Assert.assertEquals("555444333", + basicInformation.getContactDetails().getCellPhoneNumber()); + } + + @Test + public void getContactEmailTest() throws Exception { + Assert.assertEquals("wulkanowy@example.null", + basicInformation.getContactDetails().getEmail()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/user/FamilyInformationTest.java b/api/src/test/java/io/github/wulkanowy/api/user/FamilyInformationTest.java new file mode 100644 index 000000000..b4f858ece --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/user/FamilyInformationTest.java @@ -0,0 +1,59 @@ +package io.github.wulkanowy.api.user; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class FamilyInformationTest extends StudentAndParentTestCase { + + private FamilyInformation familyInformation; + + @Before + public void setUp() throws Exception { + familyInformation = new FamilyInformation(getSnp("UczenDanePodstawowe.html")); + } + + @Test + public void getFamilyMembers() throws Exception { + Assert.assertEquals(2, familyInformation.getFamilyMembers().size()); + } + + @Test + public void getNameTest() throws Exception { + List list = familyInformation.getFamilyMembers(); + Assert.assertEquals("Marianna Pająk", list.get(0).getName()); + Assert.assertEquals("Dawid Świątek", list.get(1).getName()); + } + + @Test + public void getKinshipTest() throws Exception { + List list = familyInformation.getFamilyMembers(); + Assert.assertEquals("matka", list.get(0).getKinship()); + Assert.assertEquals("ojciec", list.get(1).getKinship()); + } + + @Test + public void getAddressTest() throws Exception { + List list = familyInformation.getFamilyMembers(); + Assert.assertEquals("ul. Sportowa 16, 00-123 Warszawa", list.get(0).getAddress()); + Assert.assertEquals("ul. Sportowa 18, 00-123 Warszawa", list.get(1).getAddress()); + } + + @Test + public void getTelephonesTest() throws Exception { + List list = familyInformation.getFamilyMembers(); + Assert.assertEquals("555111222", list.get(0).getTelephones()); + Assert.assertEquals("555222111", list.get(1).getTelephones()); + } + + @Test + public void getEmailTest() throws Exception { + List list = familyInformation.getFamilyMembers(); + Assert.assertEquals("wulkanowy@example.null", list.get(0).getEmail()); + Assert.assertEquals("wulkanowy@example.null", list.get(1).getEmail()); + } +} diff --git a/api/src/test/resources/io/github/wulkanowy/api/OcenyWszystkie-semester.html b/api/src/test/resources/io/github/wulkanowy/api/OcenyWszystkie-semester.html new file mode 100644 index 000000000..f4b712c17 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/OcenyWszystkie-semester.html @@ -0,0 +1,21 @@ + + + + + Witryna ucznia i rodzica – Oceny + + +
+

Oceny

+
+
+ + +
+
+
wersja: 17.05.0000.24042
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/Start.html b/api/src/test/resources/io/github/wulkanowy/api/Start.html new file mode 100644 index 000000000..40e691610 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/Start.html @@ -0,0 +1,24 @@ + + + + + Uonet+ + + +
+ +
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-excellent.html b/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-excellent.html new file mode 100644 index 000000000..5eaf50dfe --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-excellent.html @@ -0,0 +1,408 @@ + + + + + Witryna ucznia i rodzica – Frekwencja + + + +
+

Frekwencja

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Lekcjaponiedziałek
31.08.2015
wtorek
01.09.2015
środa
02.09.2015
czwartek
03.09.2015
piątek
04.09.2015
0
1 +
+ Uroczyste rozpoczęcie roku szkolnego 2015/2016 +
+
+
+ Wychowanie do życia w rodzinie +
+
+
+ Urządzenia techniki komputerowej +
+
2 +
+ Język angielski +
+
+
+ Język niemiecki +
+
+
+ Urządzenia techniki komputerowej +
+
3 +
+ Systemy operacyjne +
+
+
+ Chemia +
+
+
+ Urządzenia techniki komputerowej +
+
4 +
+ Systemy operacyjne +
+
+
+ Geografia +
+
+
+ Urządzenia techniki komputerowej +
+
5 +
+ Tworzenie stron internetowych +
+
+
+ Matematyka +
+
+
+ Język polski +
+
6 +
+ Tworzenie stron internetowych +
+
+
+ Fizyka +
+
+
+ Matematyka +
+
7 +
+ Wychowanie fizyczne +
+
+
+ Język polski +
+
+
+ Historia +
+
8 +
+ Wychowanie fizyczne +
+
9
10
11
12
13
+

Statystyki

+
+ + +
+

Frekwencja od początku roku szkolnego: 100,00%

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IXXXIXIIIIIIIIIVVVIVIIVIIIRazem
Obecność14214313911013175126139921141211
Nieobecność nieusprawiedliwiona
Nieobecność usprawiedliwiona
Nieobecność z przyczyn szkolnych
Spóźnienie nieusprawiedliwione
Spóźnienie usprawiedliwione
Zwolnienie
+
+
wersja: 17.07.0002.24480
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-full.html b/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-full.html new file mode 100644 index 000000000..aa9953053 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-full.html @@ -0,0 +1,498 @@ + + + + + Witryna ucznia i rodzica – Frekwencja + + + +
+

Frekwencja

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Lekcjaponiedziałek
05.09.2016
wtorek
06.09.2016
środa
07.09.2016
czwartek
08.09.2016
piątek
09.09.2016
0
1 +
+ Urządzenia techniki komputerowej +
+
+
+ Multimedia i grafika komputerowa +
+
+
+ Użytkowanie urządzeń peryferyjnych komputera +
+
+
+ Religia +
+
2 +
+ Urządzenia techniki komputerowej +
+
+
+ Język niemiecki +
+
+
+ Użytkowanie urządzeń peryferyjnych komputera +
+
+
+ Język niemiecki +
+
+
+ Sieci komputerowe i administrowanie sieciami +
+
3 +
+ Urządzenia techniki komputerowej +
+
+
+ Fizyka +
+
+
+ Historia +
+
+
+ Wychowanie fizyczne +
+
+
+ Wiedza o kulturze +
+
4 +
+ Naprawa komputera +
+
+
+ Wychowanie fizyczne +
+
+
+ Język angielski +
+
+
+ Wychowanie fizyczne +
+
+
+ Język polski +
+
5 +
+ Sieci komputerowe i administrowanie sieciami +
+
+
+ Metodologia programowania +
+
+
+ Urządzenia techniki komputerowej +
+
+
+ Matematyka +
+
+
+ Metodologia programowania +
+
6 +
+ Język niemiecki +
+
+
+ Sieci komputerowe i administrowanie sieciami +
+
+
+ Język polski +
+
+
+ Podstawy przedsiębiorczości +
+
+
+ Matematyka +
+
7 +
+ Fizyka +
+
+
+ Język polski +
+
+
+ Systemy operacyjne +
+
+
+ Zajęcia z wychowawcą +
+
+
+ Religia +
+
8 +
+ Naprawa komputera +
+
+
+ Systemy operacyjne +
+
+
+ Urządzenia techniki komputerowej +
+
+
+ Zajęcia z wychowawcą +
+
9
10
11
12
13
+

Statystyki

+
+ + +
+

Frekwencja od początku roku szkolnego: 80,94%

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IXXXIXIIIIIIIIIVVVIVIIVIIIRazem
Obecność1351031085437100339010359822
Nieobecność nieusprawiedliwiona246
Nieobecność usprawiedliwiona627293044161327192
Nieobecność z przyczyn szkolnych77
Spóźnienie nieusprawiedliwione41222112
Spóźnienie usprawiedliwione11
Zwolnienie112
+
+
wersja: 17.07.0002.24480
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/exams/Sprawdziany-one-per-day.html b/api/src/test/resources/io/github/wulkanowy/api/exams/Sprawdziany-one-per-day.html new file mode 100644 index 000000000..ecc48cf16 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/exams/Sprawdziany-one-per-day.html @@ -0,0 +1,85 @@ + + + + + Witryna ucznia i rodzica – Terminarz sprawdzianów + + +
+

Sprawdziany

+

Tydzień 23.10.2017 - 29.10.2017

+
+

poniedziałek, 23.10.2017

+
+
+
Przedmiot i grupa:
+
Sieci komputerowe 3Ti|zaw2
+
+
+
Rodzaj sprawdzianu:
+
Sprawdzian
+
+
+
Opis:
+
Łącza danych
+
+
+
Nauczyciel i data wpisu:
+
Adam Wiśniewski [AW], 16.10.2017
+
+
+
+
+

wtorek, 24.10.2017

+
+
+
Przedmiot i grupa:
+
Język angielski 3Ti|J1
+
+
+
Rodzaj sprawdzianu:
+
Sprawdzian
+
+
+
Opis:
+
Czasy teraźniejsze
+
+
+
Nauczyciel i data wpisu:
+
Natalia Nowak [NN], 17.10.2017
+
+
+
+
+
+
+

piątek, 27.10.2017

+
+
+
Przedmiot i grupa:
+
Metodologia programowania 3Ti|zaw2
+
+
+
Rodzaj sprawdzianu:
+
Sprawdzian
+
+
+
Opis:
+
+
+
+
Nauczyciel i data wpisu:
+
Małgorzata Nowacka [MN], 16.10.2017
+
+
+
+
+
+ +
+
wersja: 17.08.0001.24874
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-filled.html b/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-filled.html new file mode 100644 index 000000000..a229d1d53 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-filled.html @@ -0,0 +1,108 @@ + + + + + Witryna ucznia i rodzica – Oceny + + +
+

Oceny

+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PrzedmiotOcena cząstkowaOpisWagaDataNauczyciel
ZachowanieBrak ocen
Zajęcia z wychowawcą + 5 + A1, Dzień Kobiet w naszej klasie1,0021.03.2017Patryk Maciejewski
Edukacja dla bezpieczeństwa + 4- + S1, PIERWSZA POMOC I RESUSCYTACJA5,0031.03.2017Weronika Ratajczak
Fizyka + 2 + O, Odpowiedź3,0025.06.2017Jakub Michalak
Język angielski + 5 + BW3, Writing3,0002.06.2017Oliwia Woźniak
Wiedza o społeczeństwieBrak ocen
Wychowanie fizyczne1STR8,0002.04.2017Klaudia Dziedzic
Język polski1K, Kordian5,0006.02.2017Amelia Stępień
+
+
wersja: 17.02.0000.23328
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects-average.html b/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects-average.html new file mode 100644 index 000000000..023b5405e --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects-average.html @@ -0,0 +1,70 @@ + + + + + Witryna ucznia i rodzica – Oceny + + +
+

Oceny

+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PrzedmiotOceny cząstkoweŚredniaPrzewidywana ocena rocznaOcena roczna
ZachowanieBrak ocen-bardzo dobrebardzo dobre
Język polski03,53-dobry
Wychowanie fizyczne05,05bardzo dobrycelujący
Język angielski04,44/5bardzo dobry
Wiedza o społeczeństwieBrak ocen---
+
+
wersja: 17.02.0000.23328
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects.html b/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects.html new file mode 100644 index 000000000..0f6da4148 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects.html @@ -0,0 +1,64 @@ + + + + + Witryna ucznia i rodzica – Oceny + + +
+

Oceny

+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PrzedmiotOceny cząstkowePrzewidywana ocena rocznaOcena roczna
Zachowanie-bardzo dobrebardzo dobre
Praktyka zawodowa--celujący
Metodologia programowania-bardzo dobrycelujący
Podstawy przedsiębiorczości-3/4dostateczny
Wychowanie do życia w rodzinie---
+
+
wersja: 17.05.0000.24042
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-brak-dostepu.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-brak-dostepu.html new file mode 100644 index 000000000..2911f1d24 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-brak-dostepu.html @@ -0,0 +1,15 @@ + + + + Logowanie + + +
+
+ Adres example@wulkanowy.io nie został zarejestrowany w dzienniku uczniowskim jako adres rodzica, bądź ucznia. + Jeśli jesteś rodzicem (prawnym opiekunem) ucznia (lub uczniem) szkoły korzystającej z dziennika „UONET +” udaj się do + wychowawcy i poproś o wprowadzenie Twojego adresu e-mail do Twoich danych. +
+
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-certyfikat.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-certyfikat.html new file mode 100644 index 000000000..a8496cd1b --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-certyfikat.html @@ -0,0 +1,17 @@ + + + Working... + + +
+ + + + +
+ + + diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-error.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-error.html new file mode 100644 index 000000000..087131882 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-error.html @@ -0,0 +1,18 @@ + + + + Logowanie (demo123) + + +
+
+
+ Zła nazwa użytkownika lub hasło +
+
+
+ + + diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-notLoggedIn.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-notLoggedIn.html new file mode 100644 index 000000000..f961bf82c --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-notLoggedIn.html @@ -0,0 +1,22 @@ + + + + + Dziennik UONET+ + + +
+
+
+
+
+ + Zaloguj się +
+
+
+
Uonet+ wersja 17.09.0007.26300
+
+
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-success.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-success.html new file mode 100644 index 000000000..23c27a3fe --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-success.html @@ -0,0 +1,16 @@ + + + + + Uonet+ + + +
+
+
+ example@wulkanowy.io (wyloguj) +
+
+
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/PrzerwaTechniczna.html b/api/src/test/resources/io/github/wulkanowy/api/login/PrzerwaTechniczna.html new file mode 100644 index 000000000..062f9b60c --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/login/PrzerwaTechniczna.html @@ -0,0 +1,23 @@ + + + + + Przerwa techniczna + + +
+
+
+

Przerwa techniczna

+

Aktualnie trwają prace konserwacyjne. Witryna będzie dostępna za kilka minut.

> +
+ +
+
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/cert.xml b/api/src/test/resources/io/github/wulkanowy/api/login/cert.xml new file mode 100644 index 000000000..549b2d424 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/login/cert.xml @@ -0,0 +1,18 @@ + + + + + + + Default + demo12345 + incorrect value + warszawa + asdf + asdfsdf + + + + + + diff --git a/api/src/test/resources/io/github/wulkanowy/api/messages/GetTrescWiadomosci.json b/api/src/test/resources/io/github/wulkanowy/api/messages/GetTrescWiadomosci.json new file mode 100644 index 000000000..1ba54a32c --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/messages/GetTrescWiadomosci.json @@ -0,0 +1,7 @@ +{ + "success": true, + "data": { + "Id": 12345, + "Tresc": "Witam, …. \nPozdrawiam Krzysztof Czerkas" + } +} diff --git a/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciOdebrane.json b/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciOdebrane.json new file mode 100644 index 000000000..326390ed6 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciOdebrane.json @@ -0,0 +1,35 @@ +{ + "success": true, + "data": [ + { + "Nieprzeczytana": false, + "Data": "2016-03-15 09:00:00", + "Tresc": null, + "Temat": "Wycieczka", + "NadawcaNazwa": "Kowalski Jan", + "IdWiadomosci": 1234, + "IdNadawca": 4321, + "Id": 12345 + }, + { + "Nieprzeczytana": true, + "Data": "2016-04-20 22:00:00", + "Tresc": null, + "Temat": "\"Dzień dobrego słowa\"", + "NadawcaNazwa": "Pazura Agnieszka", + "IdWiadomosci": 1235, + "IdNadawca": 12, + "Id": 12346 + }, + { + "Nieprzeczytana": false, + "Data": "2016-04-29 10:00:00", + "Tresc": null, + "Temat": "Rozdajemy oceny celujące", + "NadawcaNazwa": "Kowalski Jan", + "IdWiadomosci": 1236, + "IdNadawca": 4321, + "Id": 12347 + } + ] +} diff --git a/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciUsuniete-empty.json b/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciUsuniete-empty.json new file mode 100644 index 000000000..36f89aa1e --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciUsuniete-empty.json @@ -0,0 +1,4 @@ +{ + "success": true, + "data": [] +} diff --git a/api/src/test/resources/io/github/wulkanowy/api/messages/PageError.html b/api/src/test/resources/io/github/wulkanowy/api/messages/PageError.html new file mode 100644 index 000000000..ae976af1f --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/messages/PageError.html @@ -0,0 +1,88 @@ + + + + + + + + + + Błąd strony + + + +
+ +
+
+ Wystąpił nieoczekiwany błąd +
+
Wystąpił błąd aplikacji. Prosimy zalogować się ponownie. Jeśli problem będzie się powtarzał, prosimy o kontakt z serwisem.
+
+
+
+ +
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/messages/UndefinedError.txt b/api/src/test/resources/io/github/wulkanowy/api/messages/UndefinedError.txt new file mode 100644 index 000000000..c8d55a96c --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/messages/UndefinedError.txt @@ -0,0 +1 @@ +The custom error module does not recognize this error. \ No newline at end of file diff --git a/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-empty.html b/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-empty.html new file mode 100644 index 000000000..b15bb6e70 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-empty.html @@ -0,0 +1,20 @@ + + + + + Witryna ucznia i rodzica – Uwagi i osiągnięcia + + +
+
+

Uwagi

+

Brak informacji do wyświetlenia

+
+
+

Osiągnięcia

+

Brak informacji do wyświetlenia

+
+
+
wersja: 17.05.0000.24042
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-filled.html b/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-filled.html new file mode 100644 index 000000000..50e740585 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-filled.html @@ -0,0 +1,65 @@ + + + + + Witryna ucznia i rodzica – Uwagi i osiągnięcia + + +
+
+

Uwagi

+

06.06.2017

+
+
+
Nauczyciel:
+
Jan Kowalski [JK]
+
+
+
Kategoria:
+
Zaangażowanie społeczne
+
+
+
Treść:
+
Pomoc przy pikniku charytatywnym
+
+
+

01.12.2016

+
+
+
Nauczyciel:
+
Ochocka Zofia [PZ]
+
+
+
Kategoria:
+
Reprezentowanie szkoły
+
+
+
Treść:
+
Udział w przygotowaniu spektaklu
+
+
+

01.10.2016

+
+
+
Nauczyciel:
+
Kochański Leszek [KL]
+
+
+
Kategoria:
+
Zachowanie na lekcji
+
+
+
Treść:
+
Przeszkadzanie w prowadzeniu lekcji
+
+
+
+
+

Osiągnięcia

+
I miejsce w ogólnopolskim konkursie ortograficznym
+
III miejsce w ogólnopolskim konkursie plastycznym
+
+
+
wersja: 17.05.0000.24042
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/school/Szkola.html b/api/src/test/resources/io/github/wulkanowy/api/school/Szkola.html new file mode 100644 index 000000000..05a698a04 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/school/Szkola.html @@ -0,0 +1,136 @@ + + + + + Witryna ucznia i rodzica – Szkoła i nauczyciele + + +
+

Szkoła

+
+
+ Nazwa szkoły: + Zespół Szkół nr 64 +
+
+ Adres szkoły: + ul. Wiśniowa 128, 01-234 Rogalowo, Nibylandia +
+
+ Telefon: + 55 5555555 +
+
+ Imię i nazwisko dyrektora: + Antoni Sobczyk +
+
+ Imię i nazwisko pedagoga: + Zofia Czerwińska [ZC], Aleksander Krzemiński [AK], Karolina Kowalska [KK], Bartek Dąbrowski [BD] +
+
+

Nauczyciele

+

+ Klasa: 1a, Wychowawcy: + Karolina Kowalska [AN], Antoni Sobczyk [AS]

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Lp.PrzedmiotNauczyciel
1BiologiaKarolina Kowalska [AN]
2ChemiaZofia Czerwińska [NA]
3Edukacja dla bezpieczeństwaAleksandra Krajewska [AK]
4FizykaStanisław Krupa [BS]
5GeografiaAleksandra Wójtowicz [AW]
6HistoriaSara Wierzbicka [KB]
7Język angielskiKarolina Kowalska [AN], Mateusz Kowal [MK], Amelia Mazur [AM]
8Język niemieckiMateusz Kowal [MK], Barbara Markowska [BM]
9Język polskiMichał Mazur [MM]
10MatematykaSzymon Wojciechowski [SW]
11PlastykaMichał Mazur [MM]
12ReligiaMaja Wiśniewska [M]
13Wiedza o społeczeństwieKarolina Kowalska [AN]
14Wychowanie do życia w rodzinieZofia Czerwińska [NA]
15Wychowanie fizyczneKarolina Kowalska [AN], Liliana Kowal [LK]
16Zajęcia techniczneBartek Dąbrowski [BD]
17Zajęcia z wychowawcąKarolina Kowalska [AN]
+
+
wersja: 17.02.0000.23328
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-full.html b/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-full.html new file mode 100644 index 000000000..53eb04857 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-full.html @@ -0,0 +1,466 @@ + + + + + Witryna ucznia i rodzica – Plan lekcji + + +
+

Plan lekcji

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LekcjaPora lekcjiponiedziałek
19.06.2017
wtorek
20.06.2017
środa
21.06.2017
czwartek
22.06.2017
piątek
23.06.2017
007:10 07:55 +
+ Fizyka [zaw2] + + Bączek Grzegorz + 19 + (uczniowie zwolnieni do domu) +
+
+
+ Metodologia programowania [zaw2] + + + 32 +
+
+
+ Religia + Cyranka Krystian + 3 + Wychowanie do życia w rodzinie + Nowak Jadwiga + 3 + (zastępstwo) +
+
+
+ Język polski + + 16 + (oddział nieobecny) +
+ +
egzamin
+
+
+ Uroczyste zakończenie roku szkolnego + Baran Małgorzata + 37 +
+
108:00 08:45 +
+ Język angielski [J1] + + Kobczyk Iwona + +
+
+
+ Metodologia programowania [zaw2] + + Baran Małgorzata + 36 + (zmiana organizacji zajęć) +
+
+ Wychowanie fizyczne [zaw2] + + + G3 + (przeniesiona z lekcji 7, 01.12.2017) +
+
+
+ Użytkowanie urządzeń peryferyjnych komputera [zaw2] + + Bączek Robert + +
+
+
+ Wychowanie fizyczne [zaw1] + + Jarocki Krzysztof + G4 + Wychowanie fizyczne [zaw1] + + Nowicka Irena + G4 + (zastępstwo) +
+
208:50 09:35 +
+ Język polski + Bocian Natalia + +
+
+
+ Język niemiecki [J1] + + Rożeniec Honorata + 25 + (okienko dla uczniów) +
+
+ Język polski + Bocian Natalia + + (przeniesiona z lekcji 7, 20.06.2017) +
+
+ Język polski + Bocian Natalia + +
+
+
+ Urządzenia techniki komputerowej [zaw2] + + Bocian Grzegorz + +
+
+
+ Matematyka + Baran Małgorzata + +
+
309:40 10:25 +
+ Język polski + Bocian Natalia + +
+
+
+ Fizyka + Bączek Grzegorz + 19 + (okienko dla uczniów) +
+
+ Wychowanie fizyczne [wf2] + + Nowicka Irena + + (przeniesiona z lekcji 4, 20.06.2017) +
+
+ Wychowanie fizyczne [wf2] + + Nowicka Irena + +
+
+
+ Metodologia programowania [zaw2] + + Baran Małgorzata + +
+
+
+ Wychowanie fizyczne [wf2] + + Nowicka Irena + +
+
410:40 11:25 +
+ Urządzenia techniki komputerowej [zaw2] + + Bocian Grzegorz + +
+
+
+ Wychowanie fizyczne [wf2] + + Nowicka Irena + + (przeniesiona na lekcję 3, 20.06.2017) +
+
+
+ Matematyka + Baran Małgorzata + +
+
+
+ Wychowanie fizyczne [wf2] + + Nowicka Irena + +
+
511:30 12:15 +
+ Urządzenia techniki komputerowej [zaw2] + + Bocian Grzegorz + +
+
+
+ Podstawy przedsiębiorczości + Bogatka Anna + W12 + (okienko dla uczniów) +
+
+
+ Religia + Cyranka Krystian + +
+
+
+ Sieci komputerowe i administrowanie sieciami [zaw2] + + Rożeniec Piotr + +
+
612:20 13:05 +
+ Matematyka + Baran Małgorzata + +
+
+
+ Podstawy przedsiębiorczości + Bogatka Anna + W12 + (okienko dla uczniów) +
+
+
+ Język angielski [J1] + + Brodziec Sylwia + +
+
+
+ Religia + Cyranka Krystian + +
+
713:10 13:55 +
+ Fizyka + Bączek Grzegorz + 33 + (okienko dla uczniów) +
+
+
+ Język polski + Bocian Natalia + + (przeniesiona na lekcję 2, 20.06.2017) +
+
+
+ Multimedia i grafika komputerowa [zaw2] + + Bocian Konrad + +
+
+
+ Wiedza o kulturze + Bocian Natalia + +
+
814:00 14:45 +
+ Zajęcia z wychowawcą + Baran Małgorzata + +
+
+
+ Naprawa komputera [zaw2] + + Kraska Maciej + 32 + (okienko dla uczniów) +
+
+
+ Systemy operacyjne [zaw2] + + Kraska Maciej + 32 +
+
914:50 15:35 +
+ Język niemiecki [J1] + + Rożeniec Honorata + 25 + (uczniowie zwolnieni do domu) +
+
1015:40 16:25
1116:35 17:20
1217:25 18:10
1318:15 19:00
+
+
+
+ Kursywa- planowane +
+
+ Zwykła czcionka- zrealizowane +
+
+ Przekreślone- odwołane lub przeniesione +
+
+ Pogrubione- nowe lekcje, przeniesione z innego terminu, zastępstwa +
+
+
+
wersja: 17.05.0000.24042
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-holidays.html b/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-holidays.html new file mode 100644 index 000000000..09555cf3f --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-holidays.html @@ -0,0 +1,156 @@ + + + + + Witryna ucznia i rodzica – Plan lekcji + + +
+

Plan lekcji

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LekcjaPora lekcjiponiedziałek
31.07.2017
Ferie letnie
wtorek
01.08.2017
Ferie letnie
środa
02.08.2017
Ferie letnie
czwartek
03.08.2017
Ferie letnie
piątek
04.08.2017
Ferie letnie
007:10 07:55
108:00 08:45
208:50 09:35
309:40 10:25
410:40 11:25
511:30 12:15
612:20 13:05
713:10 13:55
814:00 14:45
914:50 15:35
1015:40 16:25
1116:35 17:20
1217:25 18:10
1318:15 19:00
+
+
+
wersja: 17.05.0000.24042
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-std.html b/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-std.html new file mode 100644 index 000000000..8bcc9794e --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-std.html @@ -0,0 +1,303 @@ + + + + + Witryna ucznia i rodzica – Plan lekcji + + +
+

Plan lekcji

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LekcjaPora lekcjiponiedziałek
19.06.2017
wtorek
20.06.2017
środa
21.06.2017
czwartek
22.06.2017
piątek
23.06.2017
108:00 08:45 +
+ Edukacja dla bezpieczeństwa + Kobczyk Iwona + +
+
+
+ Język niemiecki [JNPW] + + Dzwoniec Ewa + +
+
+
+ Wychowanie do życia w rodzinie + Baran Dominika + +
+
+
+ Język niemiecki [JNPW] + + Dzwoniec Ewa + +
+
208:50 09:35 +
+ Historia + Bogatka Katarzyna + +
+
+
+ Wychowanie fizyczne [CH] + + Brodziec Dominika + +
+
+
+ Fizyka + Bocian Łukasz + +
+
+
+ Biologia + Kowalska Anna + +
+
+
+ Religia + Kraska Maciej + +
+
309:40 10:25 +
+ Wychowanie fizyczne [CH] + + Brodziec Dominika + +
+
+
+ Język polski + Rożeniec Paulina + +
+
+
+ Matematyka + Bączek Dominika + +
+
+
+ Plastyka + Rożeniec Paulina + +
+
+
+ Zajęcia z wychowawcą + Kowalska Anna + +
+
410:30 11:15 +
+ Geografia + Orłowski Konrad + +
+
+
+ Matematyka + Bączek Dominika + +
+
+
+ Język angielski [JAPN] + + Biegus Kazimiera + +
+
+
+ Matematyka + Bączek Dominika + +
+
+
+ Historia + Bogatka Katarzyna + +
+
511:30 12:15 +
+ Matematyka + Bączek Dominika + +
+
+
+ Biologia + Kowalska Anna + +
+
+
+ Zajęcia techniczne + Chlebowski Stanisław + +
+
+
+ Język angielski [JAPN] + + Biegus Kazimiera + +
+
+
+ Język polski + Rożeniec Paulina + +
+
612:30 13:15 +
+ Matematyka + Bączek Dominika + +
+
+
+ Fizyka + Bocian Łukasz + +
+
+
+ Język polski + Rożeniec Paulina + +
+
+
+ Wychowanie fizyczne [CH] + + Brodziec Dominika + +
+
+
+ Język polski + Rożeniec Paulina + +
+
713:20 14:05 +
+ Język angielski [JAPN] + + Biegus Kazimiera + +
+
+
+ Religia + Kraska Maciej + +
+
+
+ Wychowanie fizyczne [CH] + + Brodziec Dominika + +
+
814:10 14:55
+
+
+
wersja: 17.02.0000.23328
+ + diff --git a/api/src/test/resources/io/github/wulkanowy/api/user/UczenDanePodstawowe.html b/api/src/test/resources/io/github/wulkanowy/api/user/UczenDanePodstawowe.html new file mode 100644 index 000000000..c54dd8614 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/user/UczenDanePodstawowe.html @@ -0,0 +1,119 @@ + + + + + Witryna ucznia i rodzica – Dane ucznia + + +
+

Dane podstawowe

+

Dane osobowe

+
+
+ Imię (imiona) nazwisko: + Maria Aneta Kamińska +
+
+ Data i miejsce urodzenia: + 01.01.1900, Warszawa +
+
+ PESEL: + 12345678900 +
+
+ Płeć: + Kobieta +
+
+ Obywatelstwo polskie: + Tak +
+
+ Nazwisko rodowe: + Nowak +
+
+ Imię matki i ojca: + Gabriela, Kamil +
+
+

Dane adresowe

+
+
+ Adres zamieszkania: + ul. Sportowa 16, 00-123 Warszawa +
+
+ Adres zameldowania: + ul. Sportowa 17, 00-123 Warszawa +
+
+ Adres korespondencji: + ul. Sportowa 18, 00-123 Warszawa +
+
+

Kontakt

+
+
+ Telefon: + 005554433 +
+
+ Telefon komórkowy: + 555444333 +
+
+ E-mail: + wulkanowy@example.null +
+
+

Rodzina

+
+
+ Nazwisko i imię: + Marianna Pająk +
+
+ Stopień pokrewieństwa: + matka +
+
+ Adres: + ul. Sportowa 16, 00-123 Warszawa +
+
+ Telefony: + 555111222 +
+
+ E-mail: + wulkanowy@example.null +
+
+
+
+ Nazwisko i imię: + Dawid Świątek +
+
+ Stopień pokrewieństwa: + ojciec +
+
+ Adres: + ul. Sportowa 18, 00-123 Warszawa +
+
+ Telefony: + 555222111 +
+
+ E-mail: + wulkanowy@example.null +
+
+
+
wersja: 17.02.0000.23328
+ + diff --git a/app/build.gradle b/app/build.gradle index 22af6e6bb..d930afc0d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,153 +1,109 @@ +buildscript { + repositories { + maven { url "https://plugins.gradle.org/m2/" } + maven { url 'https://maven.fabric.io/public' } + } + + dependencies { + classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' + classpath 'io.fabric.tools:gradle:1.25.1' + } +} + +repositories { + maven { url 'https://maven.fabric.io/public' } +} + apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-android-extensions' +apply plugin: 'org.greenrobot.greendao' apply plugin: 'io.fabric' -apply plugin: 'com.github.triplet.play' -apply from: 'jacoco.gradle' -apply from: 'sonarqube.gradle' +apply from: '../jacoco.gradle' +apply from: '../android-sonarqube.gradle' android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' - + compileSdkVersion 27 + buildToolsVersion "27.0.3" defaultConfig { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 - targetSdkVersion 28 - versionCode 30 - versionName "0.7.4" - multiDexEnabled true - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + targetSdkVersion 27 + versionCode 3 + versionName "0.2.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true manifestPlaceholders = [ - fabric_api_key: System.getenv("FABRIC_API_KEY") ?: "null", - crashlytics_enabled: project.hasProperty("enableCrashlytics") + fabricApiKey: System.getenv("FABRIC_API_KEY") ?: "null" ] - javaCompileOptions { - annotationProcessorOptions { - arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] - } - } - } - - sourceSets { - androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) - } - - signingConfigs { - release { - storeFile file("upload-key.jks") - storePassword System.getenv("PLAY_STORE_PASSWORD") - keyAlias System.getenv("PLAY_KEY_ALIAS") - keyPassword System.getenv("PLAY_KEY_PASSWORD") - } } buildTypes { release { - buildConfigField "boolean", "CRASHLYTICS_ENABLED", "true" - minifyEnabled true - shrinkResources true + minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.release } debug { - buildConfigField "boolean", "CRASHLYTICS_ENABLED", project.hasProperty("enableCrashlytics") ? "true" : "false" applicationIdSuffix ".dev" versionNameSuffix "-dev" testCoverageEnabled = true - ext.enableCrashlytics = project.hasProperty("enableCrashlytics") - multiDexKeepProguard file('proguard-multidex-rules.pro') + ext.enableCrashlytics = false } } - lintOptions { - disable 'HardwareIds' - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + testOptions { + unitTests.all { + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen { false } + showStandardStreams = true + } + } } } -androidExtensions { - experimental = true -} - -play { - serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") ?: "jan@fakelog.cf" - serviceAccountCredentials = file('key.p12') - defaultToAppBundles = true - track = 'alpha' +greendao { + schemaVersion 22 + generateTests = true } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('io.github.wulkanowy:api:0.7.4') { exclude module: "threetenbp" } + implementation project(':api') + implementation 'com.android.support:appcompat-v7:27.1.0' + implementation 'com.android.support:design:27.1.0' + implementation 'com.android.support:support-v4:27.1.0' + implementation 'com.android.support:recyclerview-v7:27.1.0' + implementation 'com.android.support:cardview-v7:27.1.0' + implementation 'com.android.support:customtabs:27.1.0' + implementation 'com.firebase:firebase-jobdispatcher:0.8.5' + implementation 'org.apache.commons:commons-lang3:3.7' + implementation 'eu.davidea:flexible-adapter:5.0.0-rc4' + implementation 'eu.davidea:flexible-adapter-ui:1.0.0-b1' + implementation 'org.apache.commons:commons-collections4:4.1' + implementation 'org.greenrobot:greendao:3.2.2' + implementation 'com.github.yuweiguocn:GreenDaoUpgradeHelper:v2.0.2' + implementation 'com.jakewharton:butterknife:8.8.1' + implementation 'joda-time:joda-time:2.9.9' + implementation 'com.google.dagger:dagger-android:2.14.1' + implementation 'com.google.dagger:dagger-android-support:2.14.1' + implementation 'com.aurelhubert:ahbottomnavigation:2.1.0' - implementation "androidx.legacy:legacy-support-v4:1.0.0" - implementation "androidx.appcompat:appcompat:1.0.2" - implementation "androidx.cardview:cardview:1.0.0" - implementation "com.google.android.material:material:1.0.0" - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.multidex:multidex:2.0.1' + implementation('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') { + transitive = true + } + implementation('com.crashlytics.sdk.android:answers:1.4.1@aar') { + transitive = true + } - implementation "android.arch.work:work-runtime:1.0.0" - implementation "android.arch.work:work-rxjava2:1.0.0" + 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' - implementation "androidx.room:room-runtime:2.1.0-alpha06" - implementation "androidx.room:room-rxjava2:2.1.0-alpha06" - kapt "androidx.room:room-compiler:2.1.0-alpha06" + debugImplementation 'com.amitshekhar.android:debug-db:1.0.1' + debugImplementation 'net.zetetic:android-database-sqlcipher:3.5.9' - implementation 'com.takisoft.preferencex:preferencex:1.0.0' + testImplementation 'junit:junit:4.12' + testImplementation 'org.mockito:mockito-core:2.13.0' - implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.3.3' - kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.3.3' - - implementation "com.google.dagger:dagger-android-support:2.21" - kapt "com.google.dagger:dagger-compiler:2.21" - kapt "com.google.dagger:dagger-android-processor:2.21" - - implementation "eu.davidea:flexible-adapter:5.1.0" - implementation "eu.davidea:flexible-adapter-ui:1.0.0" - implementation "com.aurelhubert:ahbottomnavigation:2.3.4" - implementation 'com.ncapdevi:frag-nav:3.2.0' - - implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f' - implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' - - implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.2' - implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' - implementation "io.reactivex.rxjava2:rxjava:2.2.7" - - implementation "com.jakewharton.threetenabp:threetenabp:1.2.0" - implementation "com.jakewharton.timber:timber:4.7.1" - implementation "at.favre.lib:slf4j-timber:1.0.1" - implementation "com.mikepenz:aboutlibraries:6.2.3" - - implementation 'com.google.firebase:firebase-core:16.0.8' - implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9' - - releaseImplementation 'fr.o80.chucker:library-no-op:2.0.4' - - debugImplementation 'fr.o80.chucker:library:2.0.4' - debugImplementation "com.amitshekhar.android:debug-db:1.0.6" - - testImplementation "junit:junit:4.12" - testImplementation "io.mockk:mockk:1.9.2" - testImplementation "org.mockito:mockito-inline:2.25.1" - testImplementation 'org.threeten:threetenbp:1.3.8' - - androidTestImplementation 'androidx.test:core:1.1.0' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.ext:junit:1.1.0' - androidTestImplementation "io.mockk:mockk-android:1.9.2" - androidTestImplementation 'org.mockito:mockito-android:2.25.1' - androidTestImplementation "androidx.room:room-testing:2.1.0-alpha06" - androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + androidTestImplementation 'com.android.support.test:runner:1.0.1' + androidTestImplementation 'org.mockito:mockito-android:2.13.0' } - -apply plugin: 'com.google.gms.google-services' diff --git a/app/jacoco.gradle b/app/jacoco.gradle deleted file mode 100644 index f2b01b483..000000000 --- a/app/jacoco.gradle +++ /dev/null @@ -1,47 +0,0 @@ -apply plugin: "jacoco" - -jacoco { - toolVersion "0.8.3" - reportsDir = file("$buildDir/reports") -} - -tasks.withType(Test) { - jacoco.includeNoLocationClasses = true -} - -task jacocoTestReport(type: JacocoReport) { - - group = "Reporting" - description = "Generate Jacoco coverage reports" - - reports { - xml.enabled = true - html.enabled = true - } - - def excludes = ['**/R.class', - '**/R$*.class', - '**/BuildConfig.*', - '**/Manifest*.*', - '**/*Test*.*', - 'android/**/*.*', - '**/*Module.*', - '**/*Dagger*.*', - '**/*MembersInjector*.*', - '**/*_Provide*Factory*.*', - '**/*_Factory.*'] - - classDirectories = fileTree( - dir: "$buildDir/intermediates/classes/debug", - excludes: excludes - ) + fileTree( - dir: "$buildDir/tmp/kotlin-classes/debug", - excludes: excludes - ) - - sourceDirectories = files("$project.projectDir/src/main/java") - executionData = fileTree( - dir: project.projectDir, - includes: ["**/*.exec", "**/*.ec"] - ) -} diff --git a/app/key-encrypted.p12 b/app/key-encrypted.p12 deleted file mode 100644 index d9811213f..000000000 Binary files a/app/key-encrypted.p12 and /dev/null differ diff --git a/app/key.p12.gpg b/app/key.p12.gpg deleted file mode 100644 index e9b6d06eb..000000000 Binary files a/app/key.p12.gpg and /dev/null differ diff --git a/app/proguard-multidex-rules.pro b/app/proguard-multidex-rules.pro deleted file mode 100644 index 9ee1737f4..000000000 --- a/app/proguard-multidex-rules.pro +++ /dev/null @@ -1,3 +0,0 @@ --keep class android.support.test.internal** { *; } --keep class org.junit.** { *; } --keep public class io.github.wulkanowy** { *; } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 15b628384..34ed4c38c 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,39 +1,25 @@ -# Optimizations --optimizationpasses 5 --optimizations !code/simplification/arithmetic,!field/*,!class/merging/* --dontusemixedcaseclassnames --dontskipnonpubliclibraryclasses --dontskipnonpubliclibraryclassmembers --dontpreverify --dontobfuscate --allowaccessmodification --repackageclasses '' --verbose +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Users\RicomenPL\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html +# Add any project specific keep options here: -#Config for anallitycs --keepattributes *Annotation* --keepattributes SourceFile,LineNumberTable --keep class com.crashlytics.** {*;} --keep public class * extends java.lang.Exception --dontwarn com.crashlytics.** +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable -#Config for OkHttp --keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase --dontwarn org.codehaus.mojo.animal_sniffer.* --dontwarn okhttp3.internal.platform.ConscryptPlatform --dontwarn javax.annotation.** - - -#Config for ReactiveNetwork --dontwarn com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork --dontwarn io.reactivex.functions.Function --dontwarn rx.internal.util.** --dontwarn sun.misc.Unsafe - -#Config for MPAndroidChart --keep class com.github.mikephil.charting.** { *; } - -#Config for API --keep class io.github.wulkanowy.api.** {*;} +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json deleted file mode 100644 index e42a4a719..000000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json +++ /dev/null @@ -1,1325 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 11, - "identityHash": "478af7daed6ac4563e71826fb70cc8c8", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "endpoint", - "columnName": "endpoint", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "loginType", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id" - ], - "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "semester_id" - ], - "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predictedGrade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "finalGrade", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "grade", - "columnName": "grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "amount", - "columnName": "amount", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semester", - "columnName": "is_semester", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"478af7daed6ac4563e71826fb70cc8c8\")" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json deleted file mode 100644 index 32f943554..000000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json +++ /dev/null @@ -1,1332 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 12, - "identityHash": "972ad26e6488d9a8239f6bd8597af61d", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "endpoint", - "columnName": "endpoint", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "loginType", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "semester_id" - ], - "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predictedGrade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "finalGrade", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "grade", - "columnName": "grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "amount", - "columnName": "amount", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semester", - "columnName": "is_semester", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"972ad26e6488d9a8239f6bd8597af61d\")" - ] - } -} \ No newline at end of file diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AccountTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AccountTest.java new file mode 100644 index 000000000..e5330b621 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AccountTest.java @@ -0,0 +1,18 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +public class AccountTest extends AbstractDaoTestLongPk { + + public AccountTest() { + super(AccountDao.class); + } + + @Override + protected Account createEntity(Long key) { + Account entity = new Account(); + entity.setId(key); + return entity; + } + +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java new file mode 100644 index 000000000..492d642d1 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java @@ -0,0 +1,28 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; +import io.github.wulkanowy.data.db.dao.entities.AttendanceLessonDao; + +public class AttendanceLessonTest extends AbstractDaoTestLongPk { + + public AttendanceLessonTest() { + super(AttendanceLessonDao.class); + } + + @Override + protected AttendanceLesson createEntity(Long key) { + AttendanceLesson entity = new AttendanceLesson(); + entity.setId(key); + entity.setIsPresence(false); + entity.setIsAbsenceUnexcused(false); + entity.setIsAbsenceExcused(false); + entity.setIsUnexcusedLateness(false); + entity.setIsAbsenceForSchoolReasons(false); + entity.setIsExcusedLateness(false); + entity.setIsExemption(false); + return entity; + } + +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DayTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DayTest.java new file mode 100644 index 000000000..34c4c4c5e --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DayTest.java @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +public class DayTest extends AbstractDaoTestLongPk { + + public DayTest() { + super(DayDao.class); + } + + @Override + protected Day createEntity(Long key) { + Day entity = new Day(); + entity.setId(key); + entity.setIsFreeDay(false); + return entity; + } + +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/GradeTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/GradeTest.java new file mode 100644 index 000000000..ea0265591 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/GradeTest.java @@ -0,0 +1,20 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +public class GradeTest extends AbstractDaoTestLongPk { + + public GradeTest() { + super(GradeDao.class); + } + + @Override + protected Grade createEntity(Long key) { + Grade entity = new Grade(); + entity.setId(key); + entity.setIsNew(false); + entity.setRead(false); + return entity; + } + +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SubjectTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SubjectTest.java new file mode 100644 index 000000000..81a2e724f --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SubjectTest.java @@ -0,0 +1,18 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +public class SubjectTest extends AbstractDaoTestLongPk { + + public SubjectTest() { + super(SubjectDao.class); + } + + @Override + protected Subject createEntity(Long key) { + Subject entity = new Subject(); + entity.setId(key); + return entity; + } + +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java new file mode 100644 index 000000000..b215b6bef --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java @@ -0,0 +1,27 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; +import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao; + +public class TimetableLessonTest extends AbstractDaoTestLongPk { + + public TimetableLessonTest() { + super(TimetableLessonDao.class); + } + + @Override + protected TimetableLesson createEntity(Long key) { + TimetableLesson entity = new TimetableLesson(); + entity.setId(key); + entity.setIsEmpty(false); + entity.setIsDivisionIntoGroups(false); + entity.setIsPlanning(false); + entity.setIsRealized(false); + entity.setIsMovedOrCanceled(false); + entity.setIsNewMovedInOrChanged(false); + return entity; + } + +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/WeekTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/WeekTest.java new file mode 100644 index 000000000..86e7a8be5 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/WeekTest.java @@ -0,0 +1,18 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +public class WeekTest extends AbstractDaoTestLongPk { + + public WeekTest() { + super(WeekDao.class); + } + + @Override + protected Week createEntity(Long key) { + Week entity = new Week(); + entity.setId(key); + return entity; + } + +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt deleted file mode 100644 index c61e9305d..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt +++ /dev/null @@ -1,159 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import android.content.ContentValues -import android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL -import androidx.room.Room -import androidx.room.testing.MigrationTestHelper -import androidx.sqlite.db.SupportSQLiteDatabase -import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import io.github.wulkanowy.data.db.AppDatabase -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class Migration12Test { - - private val dbName = "migration-test" - - @get:Rule - val helper: MigrationTestHelper = MigrationTestHelper( - InstrumentationRegistry.getInstrumentation(), - AppDatabase::class.java.canonicalName, - FrameworkSQLiteOpenHelperFactory() - ) - - @Test - fun migrate11To12_twoNotRelatedStudents() { - helper.createDatabase(dbName, 11).apply { - // user 1 - createStudent(this, 1, true) - createSemester(this, 1, false, 5, 1) - createSemester(this, 1, true, 5, 2) - - // user 2 - createStudent(this, 2, true) - createSemester(this, 2, false, 6, 1) - createSemester(this, 2, true, 6, 2) - close() - } - - helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) - - val db = getMigratedRoomDatabase() - val students = db.studentDao.loadAll().blockingGet() - - assertEquals(2, students.size) - - students[0].run { - assertEquals(1, studentId) - assertEquals(5, classId) - } - - students[1].run { - assertEquals(2, studentId) - assertEquals(6, classId) - } - } - - @Test - fun migrate11To12_removeStudentsWithoutClassId() { - helper.createDatabase(dbName, 11).apply { - // user 1 - createStudent(this, 1, true) - createSemester(this, 1, false, 0, 2) - createStudent(this, 2, true) - createSemester(this, 2, true, 1, 2) - close() - } - - helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) - - val db = getMigratedRoomDatabase() - val students = db.studentDao.loadAll().blockingGet() - - assertEquals(1, students.size) - - students[0].run { - assertEquals(2, studentId) - assertEquals(1, classId) - } - } - - @Test - fun migrate11To12_ensureThereIsOnlyOneCurrentStudent() { - helper.createDatabase(dbName, 11).apply { - // user 1 - createStudent(this, 1, true) - createSemester(this, 1, true, 5, 2) - createStudent(this, 2, true) - createSemester(this, 2, true, 6, 2) - createStudent(this, 3, true) - createSemester(this, 3, false, 7, 2) - close() - } - - helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) - - val db = getMigratedRoomDatabase() - val students = db.studentDao.loadAll().blockingGet() - - assertEquals(3, students.size) - - students[0].run { - assertEquals(studentId, 1) - assertEquals(false, isCurrent) - } - students[1].run { - assertEquals(studentId, 2) - assertEquals(false, isCurrent) - } - students[2].run { - assertEquals(studentId, 3) - assertEquals(true, isCurrent) - } - } - - private fun createStudent(db: SupportSQLiteDatabase, studentId: Int, isCurrent: Boolean) { - db.insert("Students", CONFLICT_FAIL, ContentValues().apply { - put("endpoint", "https://fakelog.cf") - put("loginType", "STANDARD") - put("email", "jan@fakelog.cf") - put("password", "******") - put("symbol", "Default") - put("student_id", studentId) - put("student_name", "Jan Kowalski") - put("school_id", "000123") - put("school_name", "") - put("is_current", isCurrent) - put("registration_date", "0") - }) - } - - private fun createSemester(db: SupportSQLiteDatabase, studentId: Int, isCurrent: Boolean, classId: Int, diaryId: Int) { - db.insert("Semesters", CONFLICT_FAIL, ContentValues().apply { - put("student_id", studentId) - put("diary_id", diaryId) - put("diary_name", "IA") - put("semester_id", diaryId * 5) - put("semester_name", "1") - put("is_current", isCurrent) - put("class_id", classId) - put("unit_id", "99") - }) - } - - private fun getMigratedRoomDatabase(): AppDatabase { - val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(), - AppDatabase::class.java, dbName) - .addMigrations(Migration12()) - .build() - // close the database and release any stream resources when the test finishes - helper.closeWhenFinished(database) - return database - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestInternetObservingStrategy.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestInternetObservingStrategy.kt deleted file mode 100644 index 7dc93c4a4..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestInternetObservingStrategy.kt +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.wulkanowy.data.repositories - -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingStrategy -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler -import io.reactivex.Observable -import io.reactivex.Single - -class TestInternetObservingStrategy : InternetObservingStrategy { - - override fun checkInternetConnectivity(host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Single { - return Single.just(true) - } - - override fun observeInternetConnectivity(initialIntervalInMs: Int, intervalInMs: Int, host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Observable { - return Observable.just(true) - } - - override fun getDefaultPingHost() = "localhost" -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt deleted file mode 100644 index ee65cf844..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendance - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class AttendanceLocalTest { - - private lateinit var attendanceLocal: AttendanceLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build() - attendanceLocal = AttendanceLocal(testDb.attendanceDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - attendanceLocal.saveAttendance(listOf( - Attendance(1, 2, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false), - Attendance(1, 2, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false), - Attendance(1, 2, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false) - )) - - val attendance = attendanceLocal - .getAttendance(Semester(1, 2, "", 1, 3, true, 1, 1), - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 14) - ) - .blockingGet() - assertEquals(2, attendance.size) - assertEquals(attendance[0].date, LocalDate.of(2018, 9, 10)) - assertEquals(attendance[1].date, LocalDate.of(2018, 9, 14)) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt deleted file mode 100644 index 014f0b8bc..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.wulkanowy.data.repositories.completedlessons - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class CompletedLessonsLocalTest { - - private lateinit var completedLessonsLocal: CompletedLessonsLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - completedLessonsLocal = CompletedLessonsLocal(testDb.completedLessonsDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - completedLessonsLocal.saveCompletedLessons(listOf( - getCompletedLesson(LocalDate.of(2018, 9, 10), 1), - getCompletedLesson(LocalDate.of(2018, 9, 14), 2), - getCompletedLesson(LocalDate.of(2018, 9, 17), 3) - )) - - val completed = completedLessonsLocal - .getCompletedLessons(Semester(1, 2, "", 1, 3, true, 1, 1), - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 14) - ) - .blockingGet() - assertEquals(2, completed.size) - assertEquals(completed[0].date, LocalDate.of(2018, 9, 10)) - assertEquals(completed[1].date, LocalDate.of(2018, 9, 14)) - } - - private fun getCompletedLesson(date: LocalDate, number: Int): CompletedLesson { - return CompletedLesson(1, 2, date, number, "", "", "", "", "", "", "") - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt deleted file mode 100644 index dc66fa428..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.data.repositories.exam - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class ExamLocalTest { - - private lateinit var examLocal: ExamLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build() - examLocal = ExamLocal(testDb.examsDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - examLocal.saveExams(listOf( - Exam(1, 2, LocalDate.of(2018, 9, 10), LocalDate.now(), "", "", "", "", "", ""), - Exam(1, 2, LocalDate.of(2018, 9, 14), LocalDate.now(), "", "", "", "", "", ""), - Exam(1, 2, LocalDate.of(2018, 9, 17), LocalDate.now(), "", "", "", "", "", "") - )) - - val exams = examLocal - .getExams(Semester(1, 2, "", 1, 3, true, 1, 1), - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 14) - ) - .blockingGet() - assertEquals(2, exams.size) - assertEquals(exams[0].date, LocalDate.of(2018, 9, 10)) - assertEquals(exams[1].date, LocalDate.of(2018, 9, 14)) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt deleted file mode 100644 index 36238f1b4..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class GradeLocalTest { - - private lateinit var gradeLocal: GradeLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - gradeLocal = GradeLocal(testDb.gradeDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - gradeLocal.saveGrades(listOf( - createGradeLocal(5, 3.0, LocalDate.of(2018, 9, 10), "", 1), - createGradeLocal(4, 4.0, LocalDate.of(2019, 2, 27), "", 2), - createGradeLocal(3, 5.0, LocalDate.of(2019, 2, 28), "", 2) - )) - - val grades = gradeLocal - .getGrades(Semester(1, 2, "", 2, 3, true, 1, 1)) - .blockingGet() - - assertEquals(2, grades.size) - assertEquals(grades[0].date, LocalDate.of(2019, 2, 27)) - assertEquals(grades[1].date, LocalDate.of(2019, 2, 28)) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt deleted file mode 100644 index 17e788fc0..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt +++ /dev/null @@ -1,112 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import android.os.Build.VERSION_CODES.P -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider.getApplicationContext -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SdkSuppress -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.impl.annotations.SpyK -import io.reactivex.Single -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate.of -import org.threeten.bp.LocalDateTime -import kotlin.test.assertFalse -import kotlin.test.assertTrue -import io.github.wulkanowy.api.grades.Grade as GradeApi - -@SdkSuppress(minSdkVersion = P) -@RunWith(AndroidJUnit4::class) -class GradeRepositoryTest { - - @SpyK - private var mockApi = Api() - - private val settings = InternetObservingSettings.builder() - .strategy(TestInternetObservingStrategy()) - .build() - - @MockK - private lateinit var semesterMock: Semester - - @MockK - private lateinit var studentMock: Student - - private lateinit var gradeRemote: GradeRemote - - private lateinit var gradeLocal: GradeLocal - - private lateinit var testDb: AppDatabase - - @Before - fun initApi() { - MockKAnnotations.init(this) - testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() - gradeLocal = GradeLocal(testDb.gradeDao) - gradeRemote = GradeRemote(mockApi) - - every { mockApi.diaryId } returns 1 - every { studentMock.registrationDate } returns LocalDateTime.of(2019, 2, 27, 12, 0) - every { semesterMock.studentId } returns 1 - every { semesterMock.semesterId } returns 1 - every { semesterMock.diaryId } returns 1 - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun markOlderThanRegisterDateAsRead() { - every { mockApi.getGrades(1) } returns Single.just(listOf( - createGradeApi(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"), - createGradeApi(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"), - createGradeApi(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"), - createGradeApi(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza") - )) - - val grades = GradeRepository(settings, gradeLocal, gradeRemote) - .getGrades(studentMock, semesterMock, true).blockingGet().sortedByDescending { it.date } - - assertFalse { grades[0].isRead } - assertFalse { grades[1].isRead } - assertTrue { grades[2].isRead } - assertTrue { grades[3].isRead } - } - - @Test - fun mitigateOldGradesNotifications() { - gradeLocal.saveGrades(listOf( - createGradeLocal(5, 3.0, of(2019, 2, 25), "Jedna ocena"), - createGradeLocal(4, 4.0, of(2019, 2, 26), "Druga"), - createGradeLocal(3, 5.0, of(2019, 2, 27), "Trzecia") - )) - - every { mockApi.getGrades(1) } returns Single.just(listOf( - createGradeApi(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"), - createGradeApi(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"), - createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"), - createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa") - )) - - val grades = GradeRepository(settings, gradeLocal, gradeRemote) - .getGrades(studentMock, semesterMock, true).blockingGet().sortedByDescending { it.date } - - assertFalse { grades[0].isRead } - assertFalse { grades[1].isRead } - assertTrue { grades[2].isRead } - assertTrue { grades[3].isRead } - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt deleted file mode 100644 index e0fd05a82..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import io.github.wulkanowy.api.toDate -import org.threeten.bp.LocalDate -import io.github.wulkanowy.api.grades.Grade as GradeRemote -import io.github.wulkanowy.data.db.entities.Grade as GradeLocal - -fun createGradeLocal(value: Int, weight: Double, date: LocalDate, desc: String, semesterId: Int = 1): GradeLocal { - return GradeLocal( - semesterId = semesterId, - studentId = 1, - modifier = .0, - teacher = "", - subject = "", - date = date, - color = "", - comment = "", - description = desc, - entry = "", - gradeSymbol = "", - value = value, - weight = "", - weightValue = weight - ) -} - -fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String): GradeRemote { - return GradeRemote().apply { - this.value = value - this.weightValue = weight - this.date = date.toDate() - this.description = desc - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt deleted file mode 100644 index 5c0590e75..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt +++ /dev/null @@ -1,68 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradestatistics - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class GradeStatisticsLocalTest { - - private lateinit var gradeStatisticsLocal: GradeStatisticsLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - gradeStatisticsLocal = GradeStatisticsLocal(testDb.gradeStatistics) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndRead_subject() { - gradeStatisticsLocal.saveGradesStatistics(listOf( - getGradeStatistics("Matematyka", 2, 1), - getGradeStatistics("Fizyka", 1, 2) - )) - - val stats = gradeStatisticsLocal.getGradesStatistics( - Semester(2, 2, "", 1, 2, true, 1, 1), false, - "Matematyka" - ).blockingGet() - assertEquals(1, stats.size) - assertEquals(stats[0].subject, "Matematyka") - } - - @Test - fun saveAndRead_all() { - gradeStatisticsLocal.saveGradesStatistics(listOf( - getGradeStatistics("Matematyka", 2, 1), - getGradeStatistics("Chemia", 2, 1), - getGradeStatistics("Fizyka", 1, 2) - )) - - val stats = gradeStatisticsLocal.getGradesStatistics( - Semester(2, 2, "", 1, 2, true, 1, 1), false, - "Wszystkie" - ).blockingGet() - assertEquals(1, stats.size) - assertEquals(stats[0].subject, "Wszystkie") - } - - private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradeStatistics { - return GradeStatistics(studentId, semesterId, subject, 5, 5, false) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt deleted file mode 100644 index a656ac058..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.wulkanowy.data.repositories.luckynumber - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class LuckyNumberLocalTest { - - private lateinit var luckyNumberLocal: LuckyNumberLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - luckyNumberLocal = LuckyNumberLocal(testDb.luckyNumberDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - luckyNumberLocal.saveLuckyNumber(LuckyNumber(1, LocalDate.of(2019, 1, 20), 14)) - - val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, true, 1, 1), - LocalDate.of(2019, 1, 20) - ).blockingGet() - - assertEquals(1, luckyNumber.studentId) - assertEquals(LocalDate.of(2019, 1, 20), luckyNumber.date) - assertEquals(14, luckyNumber.luckyNumber) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt deleted file mode 100644 index cc4dd9f31..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.data.repositories.recipient - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDateTime -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class RecipientLocalTest { - - private lateinit var recipientLocal: RecipientLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - recipientLocal = RecipientLocal(testDb.recipientDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - recipientLocal.saveRecipients(listOf( - Recipient(1, "2rPracownik", "Kowalski Jan", "Kowalski Jan [KJ] - Pracownik (Fake123456)", 3, 4, 2, "hash"), - Recipient(1, "3rPracownik", "Kowalska Karolina", "Kowalska Karolina [KK] - Pracownik (Fake123456)", 4, 4, 2, "hash"), - Recipient(1, "4rPracownik", "Krupa Stanisław", "Krupa Stanisław [KS] - Uczeń (Fake123456)", 5, 4, 1, "hash") - )) - - val recipients = recipientLocal.getRecipients( - Student("fakelog.cf", "AUTO", "", "", "", 1, "", "", "", 1, true, LocalDateTime.now()), - 2, - ReportingUnit(1, 4, "", 0, "", emptyList()) - ).blockingGet() - - assertEquals(2, recipients.size) - assertEquals(1, recipients[0].studentId) - assertEquals("3rPracownik", recipients[1].realId) - assertEquals("Kowalski Jan", recipients[0].name) - assertEquals("Kowalska Karolina [KK] - Pracownik (Fake123456)", recipients[1].realName) - assertEquals(3, recipients[0].loginId) - assertEquals(4, recipients[1].unitId) - assertEquals(2, recipients[0].role) - assertEquals("hash", recipients[1].hash) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt deleted file mode 100644 index f61c63742..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.data.repositories.student - -import android.content.Context -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.SharedPrefHelper -import io.github.wulkanowy.data.db.entities.Student -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDateTime.now -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class StudentLocalTest { - - private lateinit var studentLocal: StudentLocal - - private lateinit var testDb: AppDatabase - - private lateinit var sharedHelper: SharedPrefHelper - - @Before - fun createDb() { - val context = ApplicationProvider.getApplicationContext() - testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) - .build() - sharedHelper = SharedPrefHelper(context.getSharedPreferences("TEST", Context.MODE_PRIVATE)) - studentLocal = StudentLocal(testDb.studentDao, context) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - studentLocal.saveStudent(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now())) - .blockingGet() - - val student = studentLocal.getCurrentStudent(true).blockingGet() - assertEquals("23", student.schoolSymbol) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt deleted file mode 100644 index 438e95f48..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import io.github.wulkanowy.api.toDate -import io.github.wulkanowy.utils.toDate -import org.threeten.bp.LocalDateTime -import org.threeten.bp.LocalDateTime.now -import io.github.wulkanowy.api.timetable.Timetable as TimetableRemote -import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal - -fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", subject: String = ""): TimetableLocal { - return TimetableLocal( - studentId = 1, - diaryId = 2, - number = number, - start = start, - end = now(), - date = start.toLocalDate(), - subject = subject, - subjectOld = "", - group = "", - room = room, - roomOld = "", - teacher = "", - teacherOld = "", - info = "", - changes = false, - canceled = false - ) -} - -fun createTimetableRemote(number: Int, start: LocalDateTime, room: String, subject: String = ""): TimetableRemote { - return TimetableRemote( - number = number, - start = start.toDate(), - end = start.plusMinutes(45).toDate(), - date = start.toLocalDate().toDate(), - subject = subject, - group = "", - room = room, - teacher = "", - info = "", - changes = false, - canceled = false - ) -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt deleted file mode 100644 index 0ecbcf92e..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDateTime.of -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class TimetableLocalTest { - - private lateinit var timetableDb: TimetableLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - timetableDb = TimetableLocal(testDb.timetableDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - timetableDb.saveTimetable(listOf( - createTimetableLocal(1, of(2018, 9, 10, 0, 0, 0)), - createTimetableLocal(1, of(2018, 9, 14, 0, 0, 0)), - createTimetableLocal(1, of(2018, 9, 17, 0, 0, 0)) - )) - - val exams = timetableDb.getTimetable( - Semester(1, 2, "", 1, 1, true, 1, 1), - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 14) - ).blockingGet() - - assertEquals(2, exams.size) - assertEquals(exams[0].date, LocalDate.of(2018, 9, 10)) - assertEquals(exams[1].date, LocalDate.of(2018, 9, 14)) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt deleted file mode 100644 index 1c0802637..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import android.os.Build.VERSION_CODES.P -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider.getApplicationContext -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SdkSuppress -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.impl.annotations.SpyK -import io.reactivex.Single -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDateTime.of -import kotlin.test.assertEquals - -@SdkSuppress(minSdkVersion = P) -@RunWith(AndroidJUnit4::class) -class TimetableRepositoryTest { - - @SpyK - private var mockApi = Api() - - private val settings = InternetObservingSettings.builder() - .strategy(TestInternetObservingStrategy()) - .build() - - @MockK - private lateinit var semesterMock: Semester - - private lateinit var timetableRemote: TimetableRemote - - private lateinit var timetableLocal: TimetableLocal - - private lateinit var testDb: AppDatabase - - @Before - fun initApi() { - MockKAnnotations.init(this) - testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() - timetableLocal = TimetableLocal(testDb.timetableDao) - timetableRemote = TimetableRemote(mockApi) - - every { semesterMock.studentId } returns 1 - every { semesterMock.diaryId } returns 2 - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun copyDetailsToCompletedFromPrevious() { - timetableLocal.saveTimetable(listOf( - createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda"), - createTimetableLocal(1, of(2019, 3, 5, 8, 50), "321", "Religia"), - createTimetableLocal(1, of(2019, 3, 5, 9, 40), "213", "W-F") - )) - - every { mockApi.getTimetable(any(), any()) } returns Single.just(listOf( - createTimetableRemote(1, of(2019, 3, 5, 8, 0), "", "Przyroda"), - createTimetableRemote(1, of(2019, 3, 5, 8, 50), "", "Religia"), - createTimetableRemote(1, of(2019, 3, 5, 9, 40), "", "W-F") - )) - - val lessons = TimetableRepository(settings, timetableLocal, timetableRemote) - .getTimetable(semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true) - .blockingGet() - - assertEquals(3, lessons.size) - assertEquals("123", lessons[0].room) - assertEquals("321", lessons[1].room) - assertEquals("213", lessons[2].room) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.java b/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.java new file mode 100644 index 000000000..2fd1904ba --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.java @@ -0,0 +1,25 @@ +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 ScramblerTest { + + @Test + @SdkSuppress(minSdkVersion = 18) + public void encryptDecryptTest() throws Exception { + Context targetContext = InstrumentationRegistry.getTargetContext(); + + Assert.assertEquals("PASS", Scrambler.decrypt("TEST", + Scrambler.encrypt("TEST", "PASS", targetContext))); + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt b/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt deleted file mode 100644 index 0c47e6bb6..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.wulkanowy.utils.security - -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SdkSuppress -import androidx.test.filters.SmallTest -import org.junit.Test -import org.junit.runner.RunWith -import java.security.KeyStore -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -class ScramblerTest { - - @Test - fun encryptDecryptTest() { - assertEquals("TEST", decrypt(encrypt("TEST", - ApplicationProvider.getApplicationContext()))) - } - - @Test - fun emptyTextEncryptTest() { - assertFailsWith { - decrypt("") - } - - assertFailsWith { - encrypt("", ApplicationProvider.getApplicationContext()) - } - } - - @Test - @SdkSuppress(minSdkVersion = 18) - fun emptyKeyStoreTest() { - val text = encrypt("test", ApplicationProvider.getApplicationContext()) - - val keyStore = KeyStore.getInstance("AndroidKeyStore") - keyStore.load(null) - keyStore.deleteEntry("wulkanowy_password") - - assertFailsWith { - decrypt(text) - } - } -} diff --git a/app/src/debug/google-services.json b/app/src/debug/google-services.json deleted file mode 100644 index e9303986b..000000000 --- a/app/src/debug/google-services.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "project_info": { - "project_number": "", - "firebase_url": "", - "project_id": "", - "storage_bucket": "" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:1091101852179:android:b558a25f65d088b1", - "android_client_info": { - "package_name": "io.github.wulkanowy.dev" - } - }, - "oauth_client": [ - { - "client_id": "", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "" - } - ], - "services": { - "analytics_service": { - "status": 1 - }, - "appinvite_service": { - "status": 1, - "other_platform_oauth_client": [] - }, - "ads_service": { - "status": 2 - } - } - } - ], - "configuration_version": "1" -} diff --git a/app/src/debug/google-services.json.gpg b/app/src/debug/google-services.json.gpg deleted file mode 100644 index 736f7906d..000000000 Binary files a/app/src/debug/google-services.json.gpg and /dev/null differ diff --git a/app/src/debug/res/drawable/ic_launcher_foreground.xml b/app/src/debug/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index 6be799094..000000000 --- a/app/src/debug/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 7353dbd1f..000000000 --- a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 7353dbd1f..000000000 --- a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher.png b/app/src/debug/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 5b688d7cb..000000000 Binary files a/app/src/debug/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 81e723ecc..000000000 Binary files a/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher.png b/app/src/debug/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 48b13240c..000000000 Binary files a/app/src/debug/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 394b57076..000000000 Binary files a/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index ff8bfa3e9..000000000 Binary files a/app/src/debug/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 365b4d663..000000000 Binary files a/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 96be1ed4f..000000000 Binary files a/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 463c089b3..000000000 Binary files a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 57c7416f1..000000000 Binary files a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 53d6f5bbd..000000000 Binary files a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/debug/res/values/ic_launcher_background.xml b/app/src/debug/res/values/ic_launcher_background.xml deleted file mode 100644 index 9646c0b4e..000000000 --- a/app/src/debug/res/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #D32F2F - \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0c6edab46..f1733681e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,78 +1,54 @@ - + android:installLocation="internalOnly"> - + android:supportsRtl="true" + android:theme="@style/WulkanowyTheme"> + android:name=".ui.splash.SplashActivity" + android:configChanges="orientation|screenSize" + android:noHistory="true" + android:theme="@style/WulkanowyTheme.SplashTheme"> + - + android:label="@string/activity_dashboard_text" /> - - + android:name=".services.SyncJob" + android:exported="false"> - + - - - - + - + android:value="${fabricApiKey}" /> + diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png new file mode 100644 index 000000000..7cad5c2f4 Binary files /dev/null and b/app/src/main/ic_launcher-web.png differ diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.java b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.java new file mode 100644 index 000000000..4b8866ce2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.java @@ -0,0 +1,59 @@ +package io.github.wulkanowy; + +import android.app.Application; + +import com.crashlytics.android.Crashlytics; +import com.crashlytics.android.core.CrashlyticsCore; + +import org.greenrobot.greendao.query.QueryBuilder; + +import javax.inject.Inject; + +import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.utils.Log; +import io.fabric.sdk.android.Fabric; +import io.github.wulkanowy.data.RepositoryContract; +import io.github.wulkanowy.di.component.ApplicationComponent; +import io.github.wulkanowy.di.component.DaggerApplicationComponent; +import io.github.wulkanowy.di.modules.ApplicationModule; + +public class WulkanowyApp extends Application { + + protected ApplicationComponent applicationComponent; + + @Inject + RepositoryContract repository; + + @Override + public void onCreate() { + super.onCreate(); + applicationComponent = DaggerApplicationComponent + .builder() + .applicationModule(new ApplicationModule(this)) + .build(); + applicationComponent.inject(this); + + if (BuildConfig.DEBUG) { + enableDebugLog(); + } + initializeFabric(); + } + + private void enableDebugLog() { + QueryBuilder.LOG_VALUES = true; + FlexibleAdapter.enableLogs(Log.Level.DEBUG); + } + + private void initializeFabric() { + Fabric.with(new Fabric.Builder(this) + .kits(new Crashlytics.Builder() + .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()) + .build()) + .debuggable(BuildConfig.DEBUG) + .build()); + } + + public ApplicationComponent getApplicationComponent() { + return applicationComponent; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt deleted file mode 100644 index e427647f6..000000000 --- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt +++ /dev/null @@ -1,73 +0,0 @@ -package io.github.wulkanowy - -import android.content.Context -import androidx.appcompat.app.AppCompatDelegate -import androidx.multidex.MultiDex -import androidx.work.Configuration -import androidx.work.WorkManager -import com.crashlytics.android.Crashlytics -import com.crashlytics.android.core.CrashlyticsCore -import com.jakewharton.threetenabp.AndroidThreeTen -import dagger.android.AndroidInjector -import dagger.android.support.DaggerApplication -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.utils.Log -import io.fabric.sdk.android.Fabric -import io.github.wulkanowy.BuildConfig.DEBUG -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.di.DaggerAppComponent -import io.github.wulkanowy.services.sync.SyncWorkerFactory -import io.github.wulkanowy.utils.CrashlyticsTree -import io.github.wulkanowy.utils.DebugLogTree -import io.reactivex.exceptions.UndeliverableException -import io.reactivex.plugins.RxJavaPlugins -import timber.log.Timber -import java.io.IOException -import java.lang.Exception -import javax.inject.Inject - -class WulkanowyApp : DaggerApplication() { - - @Inject - lateinit var prefRepository: PreferencesRepository - - @Inject - lateinit var workerFactory: SyncWorkerFactory - - override fun attachBaseContext(base: Context?) { - super.attachBaseContext(base) - MultiDex.install(this) - } - - override fun onCreate() { - super.onCreate() - AndroidThreeTen.init(this) - initializeFabric() - if (DEBUG) enableDebugLog() - AppCompatDelegate.setDefaultNightMode(prefRepository.currentTheme) - WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(workerFactory).build()) - RxJavaPlugins.setErrorHandler(::onError) - } - - private fun enableDebugLog() { - Timber.plant(DebugLogTree()) - FlexibleAdapter.enableLogs(Log.Level.DEBUG) - } - - private fun initializeFabric() { - Fabric.with(Fabric.Builder(this).kits( - Crashlytics.Builder().core(CrashlyticsCore.Builder().disabled(!BuildConfig.CRASHLYTICS_ENABLED).build()).build() - ).debuggable(BuildConfig.DEBUG).build()) - Timber.plant(CrashlyticsTree()) - } - - private fun onError(t: Throwable) { - if (t is UndeliverableException && t.cause is IOException || t.cause is InterruptedException) { - Timber.e(t.cause, "An undeliverable error occurred") - } else throw t - } - - override fun applicationInjector(): AndroidInjector { - return DaggerAppComponent.builder().create(this) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt b/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt deleted file mode 100644 index b6eee316b..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.data - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Student -import java.net.URL -import javax.inject.Inject - -class ApiHelper @Inject constructor(private val api: Api) { - - fun initApi(student: Student) { - api.apply { - email = student.email - password = student.password - symbol = student.symbol - schoolSymbol = student.schoolSymbol - studentId = student.studentId - classId = student.classId - host = URL(student.endpoint).run { host + ":$port".removeSuffix(":-1") } - ssl = student.endpoint.startsWith("https") - loginType = Api.LoginType.valueOf(student.loginType) - useNewStudent = true - } - } - - fun initApi(email: String, password: String, symbol: String, endpoint: String) { - api.apply { - this.email = email - this.password = password - this.symbol = symbol - host = URL(endpoint).run { host + ":$port".removeSuffix(":-1") } - ssl = endpoint.startsWith("https") - useNewStudent = true - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/Repository.java b/app/src/main/java/io/github/wulkanowy/data/Repository.java new file mode 100644 index 000000000..d25be8b5e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/Repository.java @@ -0,0 +1,159 @@ +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.VulcanException; +import io.github.wulkanowy.data.db.dao.entities.Account; +import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; +import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.dao.entities.Grade; +import io.github.wulkanowy.data.db.dao.entities.GradeDao; +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.account.AccountSyncContract; +import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract; +import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract; +import io.github.wulkanowy.di.annotations.SyncGrades; +import io.github.wulkanowy.di.annotations.SyncSubjects; +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 AccountSyncContract accountSync; + + private final AttendanceSyncContract attendanceSync; + + private final TimetableSyncContract timetableSync; + + private final SyncContract gradeSync; + + private final SyncContract subjectSync; + + @Inject + Repository(SharedPrefContract sharedPref, + ResourcesContract resources, + DaoSession daoSession, + AccountSyncContract accountSync, + AttendanceSyncContract attendanceSync, + TimetableSyncContract timetableSync, + @SyncGrades SyncContract gradeSync, + @SyncSubjects SyncContract subjectSync) { + this.sharedPref = sharedPref; + this.resources = resources; + this.daoSession = daoSession; + this.accountSync = accountSync; + this.attendanceSync = attendanceSync; + 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 String getAttendanceLessonDescription(AttendanceLesson lesson) { + return resources.getAttendanceLessonDescription(lesson); + } + + @Override + public void registerUser(String email, String password, String symbol) throws VulcanException, + IOException, CryptoException { + accountSync.registerUser(email, password, symbol); + } + + @Override + public void initLastUser() throws VulcanException, IOException, CryptoException { + accountSync.initLastUser(); + } + + @Override + public void syncGrades() throws VulcanException, IOException, ParseException { + gradeSync.sync(); + } + + @Override + public void syncSubjects() throws VulcanException, IOException, ParseException { + subjectSync.sync(); + } + + @Override + public void syncAttendance() throws ParseException, IOException, VulcanException { + attendanceSync.syncAttendance(); + } + + @Override + public void syncAttendance(String date) throws ParseException, IOException, VulcanException { + attendanceSync.syncAttendance(date); + } + + @Override + public void syncTimetable() throws VulcanException, IOException, ParseException { + timetableSync.syncTimetable(); + } + + @Override + public void syncTimetable(String date) throws VulcanException, IOException, ParseException { + timetableSync.syncTimetable(date); + } + + @Override + public void syncAll() throws VulcanException, IOException, ParseException { + syncSubjects(); + syncGrades(); + syncAttendance(); + 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 getNewGrades() { + return daoSession.getGradeDao().queryBuilder() + .where(GradeDao.Properties.IsNew.eq(1)) + .list(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java b/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java new file mode 100644 index 000000000..f06c47620 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java @@ -0,0 +1,35 @@ +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.VulcanException; +import io.github.wulkanowy.data.db.dao.entities.Account; +import io.github.wulkanowy.data.db.dao.entities.Grade; +import io.github.wulkanowy.data.db.dao.entities.Week; +import io.github.wulkanowy.data.db.resources.ResourcesContract; +import io.github.wulkanowy.data.sync.account.AccountSyncContract; +import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract; +import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract; + +@Singleton +public interface RepositoryContract extends ResourcesContract, AccountSyncContract, + AttendanceSyncContract, TimetableSyncContract { + + long getCurrentUserId(); + + void syncGrades() throws VulcanException, IOException, ParseException; + + void syncSubjects() throws VulcanException, IOException, ParseException; + + void syncAll() throws VulcanException, IOException, ParseException; + + Account getCurrentUser(); + + Week getWeek(String date); + + List getNewGrades(); +} diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt deleted file mode 100644 index c832368a5..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt +++ /dev/null @@ -1,135 +0,0 @@ -package io.github.wulkanowy.data - -import android.content.Context -import android.content.SharedPreferences -import android.content.res.Resources -import androidx.preference.PreferenceManager -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.WalledGardenInternetObservingStrategy -import com.readystatesoftware.chuck.api.ChuckCollector -import com.readystatesoftware.chuck.api.ChuckInterceptor -import com.readystatesoftware.chuck.api.RetentionManager -import dagger.Module -import dagger.Provides -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import okhttp3.logging.HttpLoggingInterceptor -import okhttp3.logging.HttpLoggingInterceptor.Level.BASIC -import okhttp3.logging.HttpLoggingInterceptor.Level.NONE -import timber.log.Timber -import javax.inject.Singleton - -@Module -internal class RepositoryModule { - - @Singleton - @Provides - fun provideInternetObservingSettings(): InternetObservingSettings { - return InternetObservingSettings.builder() - .strategy(WalledGardenInternetObservingStrategy()) - .build() - } - - @Singleton - @Provides - fun provideApi(chuckCollector: ChuckCollector, context: Context): Api { - return Api().apply { - logLevel = NONE - androidVersion = android.os.Build.VERSION.RELEASE - buildTag = android.os.Build.MODEL - setInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { Timber.d(it) }).setLevel(BASIC)) - - // for debug only - setInterceptor(ChuckInterceptor(context, chuckCollector).maxContentLength(250000L), true, 0) - } - } - - @Singleton - @Provides - fun provideChuckCollector(context: Context, prefRepository: PreferencesRepository): ChuckCollector { - return ChuckCollector(context) - .showNotification(prefRepository.isDebugNotificationEnable) - .retentionManager(RetentionManager(context, ChuckCollector.Period.ONE_HOUR)) - } - - @Singleton - @Provides - fun provideDatabase(context: Context) = AppDatabase.newInstance(context) - - @Singleton - @Provides - fun provideResources(context: Context): Resources = context.resources - - @Singleton - @Provides - fun provideSharedPref(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - - @Singleton - @Provides - fun provideStudentDao(database: AppDatabase) = database.studentDao - - @Singleton - @Provides - fun provideSemesterDao(database: AppDatabase) = database.semesterDao - - @Singleton - @Provides - fun provideGradeDao(database: AppDatabase) = database.gradeDao - - @Singleton - @Provides - fun provideGradeSummaryDao(database: AppDatabase) = database.gradeSummaryDao - - @Singleton - @Provides - fun provideGradeStatisticsDao(database: AppDatabase) = database.gradeStatistics - - @Singleton - @Provides - fun provideMessagesDao(database: AppDatabase) = database.messagesDao - - @Singleton - @Provides - fun provideExamDao(database: AppDatabase) = database.examsDao - - @Singleton - @Provides - fun provideAttendanceDao(database: AppDatabase) = database.attendanceDao - - @Singleton - @Provides - fun provideAttendanceSummaryDao(database: AppDatabase) = database.attendanceSummaryDao - - @Singleton - @Provides - fun provideTimetableDao(database: AppDatabase) = database.timetableDao - - @Singleton - @Provides - fun provideNoteDao(database: AppDatabase) = database.noteDao - - @Singleton - @Provides - fun provideHomeworkDao(database: AppDatabase) = database.homeworkDao - - @Singleton - @Provides - fun provideSubjectDao(database: AppDatabase) = database.subjectDao - - @Singleton - @Provides - fun provideLuckyNumberDao(database: AppDatabase) = database.luckyNumberDao - - @Singleton - @Provides - fun provideCompletedLessonsDao(database: AppDatabase) = database.completedLessonsDao - - @Singleton - @Provides - fun provideReportingUnitDao(database: AppDatabase) = database.reportingUnitDao - - @Singleton - @Provides - fun provideRecipientDao(database: AppDatabase) = database.recipientDao -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt deleted file mode 100644 index 9ce62b187..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ /dev/null @@ -1,141 +0,0 @@ -package io.github.wulkanowy.data.db - -import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import androidx.room.RoomDatabase.JournalMode.TRUNCATE -import androidx.room.TypeConverters -import io.github.wulkanowy.data.db.dao.AttendanceDao -import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao -import io.github.wulkanowy.data.db.dao.CompletedLessonsDao -import io.github.wulkanowy.data.db.dao.ExamDao -import io.github.wulkanowy.data.db.dao.GradeDao -import io.github.wulkanowy.data.db.dao.GradeStatisticsDao -import io.github.wulkanowy.data.db.dao.GradeSummaryDao -import io.github.wulkanowy.data.db.dao.HomeworkDao -import io.github.wulkanowy.data.db.dao.LuckyNumberDao -import io.github.wulkanowy.data.db.dao.MessagesDao -import io.github.wulkanowy.data.db.dao.NoteDao -import io.github.wulkanowy.data.db.dao.RecipientDao -import io.github.wulkanowy.data.db.dao.ReportingUnitDao -import io.github.wulkanowy.data.db.dao.SemesterDao -import io.github.wulkanowy.data.db.dao.StudentDao -import io.github.wulkanowy.data.db.dao.SubjectDao -import io.github.wulkanowy.data.db.dao.TimetableDao -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.db.entities.Subject -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.data.db.migrations.Migration10 -import io.github.wulkanowy.data.db.migrations.Migration11 -import io.github.wulkanowy.data.db.migrations.Migration12 -import io.github.wulkanowy.data.db.migrations.Migration2 -import io.github.wulkanowy.data.db.migrations.Migration3 -import io.github.wulkanowy.data.db.migrations.Migration4 -import io.github.wulkanowy.data.db.migrations.Migration5 -import io.github.wulkanowy.data.db.migrations.Migration6 -import io.github.wulkanowy.data.db.migrations.Migration7 -import io.github.wulkanowy.data.db.migrations.Migration8 -import io.github.wulkanowy.data.db.migrations.Migration9 -import javax.inject.Singleton - -@Singleton -@Database( - entities = [ - Student::class, - Semester::class, - Exam::class, - Timetable::class, - Attendance::class, - AttendanceSummary::class, - Grade::class, - GradeSummary::class, - GradeStatistics::class, - Message::class, - Note::class, - Homework::class, - Subject::class, - LuckyNumber::class, - CompletedLesson::class, - ReportingUnit::class, - Recipient::class - ], - version = AppDatabase.VERSION_SCHEMA, - exportSchema = true -) -@TypeConverters(Converters::class) -abstract class AppDatabase : RoomDatabase() { - - companion object { - const val VERSION_SCHEMA = 12 - - fun newInstance(context: Context): AppDatabase { - return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database") - .setJournalMode(TRUNCATE) - .fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1) - .fallbackToDestructiveMigrationOnDowngrade() - .addMigrations( - Migration2(), - Migration3(), - Migration4(), - Migration5(), - Migration6(), - Migration7(), - Migration8(), - Migration9(), - Migration10(), - Migration11(), - Migration12() - ) - .build() - } - } - - abstract val studentDao: StudentDao - - abstract val semesterDao: SemesterDao - - abstract val examsDao: ExamDao - - abstract val timetableDao: TimetableDao - - abstract val attendanceDao: AttendanceDao - - abstract val attendanceSummaryDao: AttendanceSummaryDao - - abstract val gradeDao: GradeDao - - abstract val gradeSummaryDao: GradeSummaryDao - - abstract val gradeStatistics: GradeStatisticsDao - - abstract val messagesDao: MessagesDao - - abstract val noteDao: NoteDao - - abstract val homeworkDao: HomeworkDao - - abstract val subjectDao: SubjectDao - - abstract val luckyNumberDao: LuckyNumberDao - - abstract val completedLessonsDao: CompletedLessonsDao - - abstract val reportingUnitDao: ReportingUnitDao - - abstract val recipientDao: RecipientDao -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt deleted file mode 100644 index 73a04d236..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt +++ /dev/null @@ -1,51 +0,0 @@ -package io.github.wulkanowy.data.db - -import androidx.room.TypeConverter -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken -import org.threeten.bp.DateTimeUtils -import org.threeten.bp.Instant -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDateTime -import org.threeten.bp.Month -import org.threeten.bp.ZoneOffset -import java.util.Date - -class Converters { - - @TypeConverter - fun timestampToDate(value: Long?): LocalDate? = value?.run { - DateTimeUtils.toInstant(Date(value)).atZone(ZoneOffset.UTC).toLocalDate() - } - - @TypeConverter - fun dateToTimestamp(date: LocalDate?): Long? { - return date?.atStartOfDay()?.toInstant(ZoneOffset.UTC)?.toEpochMilli() - } - - @TypeConverter - fun timestampToTime(value: Long?): LocalDateTime? = value?.let { - LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.UTC) - } - - @TypeConverter - fun timeToTimestamp(date: LocalDateTime?): Long? { - return date?.atZone(ZoneOffset.UTC)?.toInstant()?.toEpochMilli() - } - - @TypeConverter - fun monthToInt(month: Month?) = month?.value - - @TypeConverter - fun intToMonth(value: Int?) = value?.let { Month.of(it) } - - @TypeConverter - fun intListToGson(list: List): String { - return Gson().toJson(list) - } - - @TypeConverter - fun gsonToIntList(value: String): List { - return Gson().fromJson(value, object : TypeToken>() {}.type) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt deleted file mode 100644 index b3b6f5e3e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.db - -import android.annotation.SuppressLint -import android.content.SharedPreferences -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SharedPrefHelper @Inject constructor(private val sharedPref: SharedPreferences) { - - @SuppressLint("ApplySharedPref") - fun putLong(key: String, value: Long, sync: Boolean = false) { - sharedPref.edit().putLong(key, value).apply { - if (sync) commit() else apply() - } - } - - fun getLong(key: String, defaultValue: Long): Long { - return sharedPref.getLong(key, defaultValue) - } - - fun delete(key: String) { - sharedPref.edit().remove(key).apply() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt deleted file mode 100644 index d3c4f146f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Attendance -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Singleton - -@Singleton -@Dao -interface AttendanceDao { - - @Insert - fun insertAll(exams: List): List - - @Delete - fun deleteAll(exams: List) - - @Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") - fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt deleted file mode 100644 index a7413de56..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.reactivex.Maybe - -@Dao -interface AttendanceSummaryDao { - - @Insert - fun insertAll(exams: List): List - - @Delete - fun deleteAll(exams: List) - - @Query("SELECT * FROM AttendanceSummary WHERE diary_id = :diaryId AND student_id = :studentId AND subject_id = :subjectId") - fun loadAll(diaryId: Int, studentId: Int, subjectId: Int): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt deleted file mode 100644 index 6816ceaaf..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Singleton - -@Singleton -@Dao -interface CompletedLessonsDao { - - @Insert - fun insertAll(exams: List) - - @Delete - fun deleteAll(exams: List) - - @Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") - fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java new file mode 100644 index 000000000..a67e9b7f5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java @@ -0,0 +1,61 @@ +package io.github.wulkanowy.data.db.dao; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; + +import com.github.yuweiguocn.library.greendao.MigrationHelper; + +import org.greenrobot.greendao.database.Database; +import org.greenrobot.greendao.database.StandardDatabase; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.github.wulkanowy.BuildConfig; +import io.github.wulkanowy.data.db.dao.entities.AccountDao; +import io.github.wulkanowy.data.db.dao.entities.DaoMaster; +import io.github.wulkanowy.data.db.dao.entities.GradeDao; +import io.github.wulkanowy.data.db.dao.entities.SubjectDao; +import io.github.wulkanowy.data.db.shared.SharedPrefContract; +import io.github.wulkanowy.di.annotations.ApplicationContext; +import io.github.wulkanowy.di.annotations.DatabaseInfo; +import io.github.wulkanowy.utils.LogUtils; + +@Singleton +public class DbHelper extends DaoMaster.OpenHelper { + + private SharedPrefContract sharedPref; + + @Inject + DbHelper(@ApplicationContext Context context, @DatabaseInfo String dbName, + SharedPrefContract sharedPref) { + super(context, dbName); + this.sharedPref = sharedPref; + } + + @Override + @SuppressWarnings("unchecked") + public void onUpgrade(Database db, int oldVersion, int newVersion) { + MigrationHelper.DEBUG = BuildConfig.DEBUG; + MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() { + @Override + public void onCreateAllTables(Database db, boolean ifNotExists) { + DaoMaster.createAllTables(db, ifNotExists); + } + @Override + public void onDropAllTables(Database db, boolean ifExists) { + DaoMaster.dropAllTables(db, ifExists); + } + }, AccountDao.class, SubjectDao.class, GradeDao.class); + } + + @Override + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + Database database = new StandardDatabase(db); + DaoMaster.dropAllTables(database, true); + onCreate(database); + sharedPref.setCurrentUserId(0); + + LogUtils.info("Cleaning user data oldVersion=" + oldVersion + " newVersion=" + newVersion); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt deleted file mode 100644 index 06cd56135..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Exam -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Singleton - -@Singleton -@Dao -interface ExamDao { - - @Insert - fun insertAll(exams: List): List - - @Delete - fun deleteAll(exams: List) - - @Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") - fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt deleted file mode 100644 index 0bd210b02..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import androidx.room.Update -import io.github.wulkanowy.data.db.entities.Grade -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface GradeDao { - - @Insert - fun insertAll(grades: List) - - @Update - fun updateAll(grade: List) - - @Delete - fun deleteAll(grades: List) - - @Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId") - fun loadAll(semesterId: Int, studentId: Int): Maybe> - -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt deleted file mode 100644 index 338c369fa..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface GradeStatisticsDao { - - @Insert - fun insertAll(gradesStatistics: List) - - @Delete - fun deleteAll(gradesStatistics: List) - - @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester") - fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): Maybe> - - @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND is_semester = :isSemester") - fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt deleted file mode 100644 index 3f2e87bd0..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface GradeSummaryDao { - - @Insert - fun insertAll(gradesSummary: List) - - @Delete - fun deleteAll(gradesSummary: List) - - @Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId") - fun loadAll(semesterId: Int, studentId: Int): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt deleted file mode 100644 index 253bdb11f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Homework -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Singleton - -@Singleton -@Dao -interface HomeworkDao { - - @Insert - fun insertAll(homework: List) - - @Delete - fun deleteAll(homework: List) - - @Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date >= :from AND date <= :end") - fun loadAll(semesterId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt deleted file mode 100644 index afd7905c0..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import androidx.room.Update -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Singleton - -@Singleton -@Dao -interface LuckyNumberDao { - - @Insert - fun insert(luckyNumber: LuckyNumber) - - @Update - fun update(luckyNumber: LuckyNumber) - - @Delete - fun delete(luckyNumber: LuckyNumber) - - @Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date") - fun load(studentId: Int, date: LocalDate): Maybe - -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt deleted file mode 100644 index 3ef5d6905..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import androidx.room.Update -import io.github.wulkanowy.data.db.entities.Message -import io.reactivex.Maybe - -@Dao -interface MessagesDao { - - @Insert - fun insertAll(messages: List) - - @Delete - fun deleteAll(messages: List) - - @Update - fun updateAll(messages: List) - - @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC") - fun loadAll(studentId: Int, folder: Int): Maybe> - - @Query("SELECT * FROM Messages WHERE student_id = :studentId AND real_id = :id") - fun load(studentId: Int, id: Int): Maybe - - @Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC") - fun loadDeleted(studentId: Int): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt deleted file mode 100644 index 867e06a25..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import androidx.room.Update -import io.github.wulkanowy.data.db.entities.Note -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface NoteDao { - - @Insert - fun insertAll(notes: List) - - @Update - fun updateAll(notes: List) - - @Delete - fun deleteAll(notes: List) - - @Query("SELECT * FROM Notes WHERE student_id = :studentId") - fun loadAll(studentId: Int): Maybe> - -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt deleted file mode 100644 index 7c5fd6ca6..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Recipient -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface RecipientDao { - - @Insert - fun insertAll(messages: List) - - @Delete - fun deleteAll(messages: List) - - @Query("SELECT * FROM Recipients WHERE student_id = :studentId AND role = :role AND unit_id = :unitId") - fun load(studentId: Int, role: Int, unitId: Int): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt deleted file mode 100644 index 1898390a9..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface ReportingUnitDao { - - @Insert - fun insertAll(reportingUnits: List) - - @Delete - fun deleteAll(reportingUnits: List) - - @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId") - fun load(studentId: Int): Maybe> - - @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId") - fun loadOne(studentId: Int, unitId: Int): Maybe -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt deleted file mode 100644 index 01841fb67..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface SemesterDao { - - @Insert - fun insertAll(semester: List) - - @Delete - fun deleteAll(semester: List) - - @Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId") - fun loadAll(studentId: Int, classId: Int): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt deleted file mode 100644 index 8110bb696..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy.ABORT -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface StudentDao { - - @Insert(onConflict = ABORT) - fun insert(student: Student): Long - - @Delete - fun delete(student: Student) - - @Query("SELECT * FROM Students WHERE is_current = 1") - fun loadCurrent(): Maybe - - @Query("SELECT * FROM Students") - fun loadAll(): Maybe> - - @Query("UPDATE Students SET is_current = 1 WHERE id = :id") - fun updateCurrent(id: Long) - - @Query("UPDATE Students SET is_current = 0") - fun resetCurrent() -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt deleted file mode 100644 index 725a371ab..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Subject -import io.reactivex.Maybe - -@Dao -interface SubjectDao { - - @Insert - fun insertAll(subjects: List): List - - @Delete - fun deleteAll(subjects: List) - - @Query("SELECT * FROM Subjects WHERE diary_id = :diaryId AND student_id = :studentId") - fun loadAll(diaryId: Int, studentId: Int): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt deleted file mode 100644 index abe213618..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Timetable -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Singleton - -@Singleton -@Dao -interface TimetableDao { - - @Insert - fun insertAll(exams: List): List - - @Delete - fun deleteAll(exams: List) - - @Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") - fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java new file mode 100644 index 000000000..20a47ac26 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java @@ -0,0 +1,258 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.DaoException; +import org.greenrobot.greendao.annotation.Entity; +import org.greenrobot.greendao.annotation.Generated; +import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.Property; +import org.greenrobot.greendao.annotation.ToMany; + +import java.util.List; + +@Entity( + nameInDb = "Accounts", + active = true +) +public class Account { + + @Id(autoincrement = true) + private Long id; + + @Property(nameInDb = "NAME") + private String name; + + @Property(nameInDb = "E-MAIL") + private String email; + + @Property(nameInDb = "PASSWORD") + private String password; + + @Property(nameInDb = "SYMBOL") + private String symbol; + + @Property(nameInDb = "SNPID") + private String snpId; + + @ToMany(referencedJoinProperty = "userId") + private List subjectList; + + @ToMany(referencedJoinProperty = "userId") + private List gradeList; + + @ToMany(referencedJoinProperty = "userId") + private List dayList; + + /** + * Used to resolve relations + */ + @Generated(hash = 2040040024) + private transient DaoSession daoSession; + + /** + * Used for active entity operations. + */ + @Generated(hash = 335469827) + private transient AccountDao myDao; + + @Generated(hash = 735765217) + public Account(Long id, String name, String email, String password, String symbol, + String snpId) { + this.id = id; + this.name = name; + this.email = email; + this.password = password; + this.symbol = symbol; + this.snpId = snpId; + } + + @Generated(hash = 882125521) + public Account() { + } + + public Long getId() { + return id; + } + + public Account setId(Long id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public Account setName(String name) { + this.name = name; + return this; + } + + public String getEmail() { + return email; + } + + public Account setEmail(String email) { + this.email = email; + return this; + } + + public String getPassword() { + return password; + } + + public Account setPassword(String password) { + this.password = password; + return this; + } + + public String getSymbol() { + return symbol; + } + + public Account setSymbol(String symbol) { + this.symbol = symbol; + return this; + } + + public String getSnpId() { + return this.snpId; + } + + public Account setSnpId(String snpId) { + this.snpId = snpId; + return this; + } + + /** + * To-many relationship, resolved on first access (and after reset). + * Changes to to-many relations are not persisted, make changes to the target entity. + */ + @Generated(hash = 1800750450) + public List getSubjectList() { + if (subjectList == null) { + final DaoSession daoSession = this.daoSession; + if (daoSession == null) { + throw new DaoException("Entity is detached from DAO context"); + } + SubjectDao targetDao = daoSession.getSubjectDao(); + List subjectListNew = targetDao._queryAccount_SubjectList(id); + synchronized (this) { + if (subjectList == null) { + subjectList = subjectListNew; + } + } + } + return subjectList; + } + + /** + * Resets a to-many relationship, making the next get call to query for a fresh result. + */ + @Generated(hash = 594294258) + public synchronized void resetSubjectList() { + subjectList = null; + } + + /** + * To-many relationship, resolved on first access (and after reset). + * Changes to to-many relations are not persisted, make changes to the target entity. + */ + @Generated(hash = 1040074549) + public List getGradeList() { + if (gradeList == null) { + final DaoSession daoSession = this.daoSession; + if (daoSession == null) { + throw new DaoException("Entity is detached from DAO context"); + } + GradeDao targetDao = daoSession.getGradeDao(); + List gradeListNew = targetDao._queryAccount_GradeList(id); + synchronized (this) { + if (gradeList == null) { + gradeList = gradeListNew; + } + } + } + return gradeList; + } + + /** + * Resets a to-many relationship, making the next get call to query for a fresh result. + */ + @Generated(hash = 1939990047) + public synchronized void resetGradeList() { + gradeList = null; + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 128553479) + public void delete() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.delete(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 1942392019) + public void refresh() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.refresh(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 713229351) + public void update() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.update(this); + } + + /** + * To-many relationship, resolved on first access (and after reset). + * Changes to to-many relations are not persisted, make changes to the target entity. + */ + @Generated(hash = 300459794) + public List getDayList() { + if (dayList == null) { + final DaoSession daoSession = this.daoSession; + if (daoSession == null) { + throw new DaoException("Entity is detached from DAO context"); + } + DayDao targetDao = daoSession.getDayDao(); + List dayListNew = targetDao._queryAccount_DayList(id); + synchronized (this) { + if (dayList == null) { + dayList = dayListNew; + } + } + } + return dayList; + } + + /** + * Resets a to-many relationship, making the next get call to query for a fresh result. + */ + @Generated(hash = 1010399236) + public synchronized void resetDayList() { + dayList = null; + } + + /** called by internal mechanisms, do not call yourself. */ + @Generated(hash = 1812283172) + public void __setDaoSession(DaoSession daoSession) { + this.daoSession = daoSession; + myDao = daoSession != null ? daoSession.getAccountDao() : null; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java new file mode 100644 index 000000000..9250518af --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java @@ -0,0 +1,254 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.DaoException; +import org.greenrobot.greendao.annotation.Entity; +import org.greenrobot.greendao.annotation.Generated; +import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.Index; +import org.greenrobot.greendao.annotation.Property; +import org.greenrobot.greendao.annotation.Transient; + +import java.io.Serializable; + +@Entity( + nameInDb = "AttendanceLessons", + active = true, + indexes = {@Index(value = "dayId,date,number", unique = true)} +) +public class AttendanceLesson implements Serializable { + + @Id(autoincrement = true) + private Long id; + + @Property(nameInDb = "DAY_ID") + private Long dayId; + + @Property(nameInDb = "DATE") + private String date = ""; + + @Property(nameInDb = "NUMBER_OF_LESSON") + private int number = 0; + + @Property(nameInDb = "SUBJECT_NAME") + private String subject = ""; + + @Property(nameInDb = "IS_PRESENCE") + private boolean isPresence = false; + + @Property(nameInDb = "IS_ABSENCE_UNEXCUSED") + private boolean isAbsenceUnexcused = false; + + @Property(nameInDb = "IS_ABSENCE_EXCUSED") + private boolean isAbsenceExcused = false; + + @Property(nameInDb = "IS_UNEXCUSED_LATENESS") + private boolean isUnexcusedLateness = false; + + @Property(nameInDb = "IS_ABSENCE_FOR_SCHOOL_REASONS") + private boolean isAbsenceForSchoolReasons = false; + + @Property(nameInDb = "IS_EXCUSED_LATENESS") + private boolean isExcusedLateness = false; + + @Property(nameInDb = "IS_EXEMPTION") + private boolean isExemption = false; + + @Transient + private String description = ""; + + private static final long serialVersionUID = 42L; + + /** + * Used to resolve relations + */ + @Generated(hash = 2040040024) + private transient DaoSession daoSession; + + /** + * Used for active entity operations. + */ + @Generated(hash = 1936953859) + private transient AttendanceLessonDao myDao; + + @Generated(hash = 1428129046) + public AttendanceLesson(Long id, Long dayId, String date, int number, + String subject, boolean isPresence, boolean isAbsenceUnexcused, + boolean isAbsenceExcused, boolean isUnexcusedLateness, + boolean isAbsenceForSchoolReasons, boolean isExcusedLateness, + boolean isExemption) { + this.id = id; + this.dayId = dayId; + this.date = date; + this.number = number; + this.subject = subject; + this.isPresence = isPresence; + this.isAbsenceUnexcused = isAbsenceUnexcused; + this.isAbsenceExcused = isAbsenceExcused; + this.isUnexcusedLateness = isUnexcusedLateness; + this.isAbsenceForSchoolReasons = isAbsenceForSchoolReasons; + this.isExcusedLateness = isExcusedLateness; + this.isExemption = isExemption; + } + + @Generated(hash = 921806575) + public AttendanceLesson() { + } + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getDayId() { + return this.dayId; + } + + public void setDayId(Long dayId) { + this.dayId = dayId; + } + + public String getDate() { + return this.date; + } + + public AttendanceLesson setDate(String date) { + this.date = date; + return this; + } + + public int getNumber() { + return this.number; + } + + public AttendanceLesson setNumber(int number) { + this.number = number; + return this; + } + + public String getSubject() { + return this.subject; + } + + public AttendanceLesson setSubject(String subject) { + this.subject = subject; + return this; + } + + public boolean getIsPresence() { + return this.isPresence; + } + + public AttendanceLesson setIsPresence(boolean isPresence) { + this.isPresence = isPresence; + return this; + } + + public boolean getIsAbsenceUnexcused() { + return this.isAbsenceUnexcused; + } + + public AttendanceLesson setIsAbsenceUnexcused(boolean isAbsenceUnexcused) { + this.isAbsenceUnexcused = isAbsenceUnexcused; + return this; + } + + public boolean getIsAbsenceExcused() { + return this.isAbsenceExcused; + } + + public AttendanceLesson setIsAbsenceExcused(boolean isAbsenceExcused) { + this.isAbsenceExcused = isAbsenceExcused; + return this; + } + + public boolean getIsUnexcusedLateness() { + return this.isUnexcusedLateness; + } + + public AttendanceLesson setIsUnexcusedLateness(boolean isUnexcusedLateness) { + this.isUnexcusedLateness = isUnexcusedLateness; + return this; + } + + public boolean getIsAbsenceForSchoolReasons() { + return this.isAbsenceForSchoolReasons; + } + + public AttendanceLesson setIsAbsenceForSchoolReasons(boolean isAbsenceForSchoolReasons) { + this.isAbsenceForSchoolReasons = isAbsenceForSchoolReasons; + return this; + } + + public boolean getIsExcusedLateness() { + return this.isExcusedLateness; + } + + public AttendanceLesson setIsExcusedLateness(boolean isExcusedLateness) { + this.isExcusedLateness = isExcusedLateness; + return this; + } + + public boolean getIsExemption() { + return this.isExemption; + } + + public AttendanceLesson setIsExemption(boolean isExemption) { + this.isExemption = isExemption; + return this; + } + + public String getDescription() { + return description; + } + + public AttendanceLesson setDescription(String description) { + this.description = description; + return this; + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 128553479) + public void delete() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.delete(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 1942392019) + public void refresh() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.refresh(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 713229351) + public void update() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.update(this); + } + + /** called by internal mechanisms, do not call yourself. */ + @Generated(hash = 1157101112) + public void __setDaoSession(DaoSession daoSession) { + this.daoSession = daoSession; + myDao = daoSession != null ? daoSession.getAttendanceLessonDao() : null; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java new file mode 100644 index 000000000..fb3ce4143 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java @@ -0,0 +1,239 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.DaoException; +import org.greenrobot.greendao.annotation.Entity; +import org.greenrobot.greendao.annotation.Generated; +import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.Index; +import org.greenrobot.greendao.annotation.OrderBy; +import org.greenrobot.greendao.annotation.Property; +import org.greenrobot.greendao.annotation.ToMany; + +import java.util.List; + +@Entity( + nameInDb = "Days", + active = true, + indexes = {@Index(value = "userId,weekId,date", unique = true)} +) +public class Day { + + @Id(autoincrement = true) + private Long id; + + @Property(nameInDb = "USER_ID") + private Long userId; + + @Property(nameInDb = "WEEK_ID") + private Long weekId; + + @Property(nameInDb = "DATE") + private String date = ""; + + @Property(nameInDb = "DAY_NAME") + private String dayName = ""; + + @Property(nameInDb = "IS_FREE_DAY") + private boolean isFreeDay = false; + + @Property(nameInDb = "FREE_DAY_NAME") + private String freeDayName = ""; + + @ToMany(referencedJoinProperty = "dayId") + private List timetableLessons; + + @ToMany(referencedJoinProperty = "dayId") + @OrderBy("number ASC") + private List attendanceLessons; + + /** + * Used to resolve relations + */ + @Generated(hash = 2040040024) + private transient DaoSession daoSession; + + /** + * Used for active entity operations. + */ + @Generated(hash = 312167767) + private transient DayDao myDao; + + @Generated(hash = 723729681) + public Day(Long id, Long userId, Long weekId, String date, String dayName, + boolean isFreeDay, String freeDayName) { + this.id = id; + this.userId = userId; + this.weekId = weekId; + this.date = date; + this.dayName = dayName; + this.isFreeDay = isFreeDay; + this.freeDayName = freeDayName; + } + + @Generated(hash = 866989762) + public Day() { + } + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getUserId() { + return userId; + } + + public Long getWeekId() { + return weekId; + } + + public Day setWeekId(Long weekId) { + this.weekId = weekId; + return this; + } + + public Day setUserId(Long userId) { + this.userId = userId; + return this; + } + + public String getDate() { + return date; + } + + public Day setDate(String date) { + this.date = date; + return this; + } + + public String getDayName() { + return dayName; + } + + public Day setDayName(String dayName) { + this.dayName = dayName; + return this; + } + + public boolean getIsFreeDay() { + return this.isFreeDay; + } + + public Day setIsFreeDay(boolean isFreeDay) { + this.isFreeDay = isFreeDay; + return this; + } + + public String getFreeDayName() { + return freeDayName; + } + + public Day setFreeDayName(String freeDayName) { + this.freeDayName = freeDayName; + return this; + } + + /** + * To-many relationship, resolved on first access (and after reset). + * Changes to to-many relations are not persisted, make changes to the target entity. + */ + @Generated(hash = 218588195) + public List getTimetableLessons() { + if (timetableLessons == null) { + final DaoSession daoSession = this.daoSession; + if (daoSession == null) { + throw new DaoException("Entity is detached from DAO context"); + } + TimetableLessonDao targetDao = daoSession.getTimetableLessonDao(); + List timetableLessonsNew = targetDao + ._queryDay_TimetableLessons(id); + synchronized (this) { + if (timetableLessons == null) { + timetableLessons = timetableLessonsNew; + } + } + } + return timetableLessons; + } + + /** Resets a to-many relationship, making the next get call to query for a fresh result. */ + @Generated(hash = 1687683740) + public synchronized void resetTimetableLessons() { + timetableLessons = null; + } + + /** + * To-many relationship, resolved on first access (and after reset). + * Changes to to-many relations are not persisted, make changes to the target entity. + */ + @Generated(hash = 1166820581) + public List getAttendanceLessons() { + if (attendanceLessons == null) { + final DaoSession daoSession = this.daoSession; + if (daoSession == null) { + throw new DaoException("Entity is detached from DAO context"); + } + AttendanceLessonDao targetDao = daoSession.getAttendanceLessonDao(); + List attendanceLessonsNew = targetDao + ._queryDay_AttendanceLessons(id); + synchronized (this) { + if (attendanceLessons == null) { + attendanceLessons = attendanceLessonsNew; + } + } + } + return attendanceLessons; + } + + /** Resets a to-many relationship, making the next get call to query for a fresh result. */ + @Generated(hash = 1343075564) + public synchronized void resetAttendanceLessons() { + attendanceLessons = null; + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 128553479) + public void delete() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.delete(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 1942392019) + public void refresh() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.refresh(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 713229351) + public void update() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.update(this); + } + + /** called by internal mechanisms, do not call yourself. */ + @Generated(hash = 1409317752) + public void __setDaoSession(DaoSession daoSession) { + this.daoSession = daoSession; + myDao = daoSession != null ? daoSession.getDayDao() : null; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Grade.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Grade.java new file mode 100644 index 000000000..632f9bcc7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Grade.java @@ -0,0 +1,331 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.greenrobot.greendao.DaoException; +import org.greenrobot.greendao.annotation.Entity; +import org.greenrobot.greendao.annotation.Generated; +import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.Property; + +import java.io.Serializable; + +import io.github.wulkanowy.R; + +@Entity( + nameInDb = "Grades", + active = true +) +public class Grade implements Serializable { + + @Id(autoincrement = true) + protected Long id; + + @Property(nameInDb = "SUBJECT_ID") + private Long subjectId; + + @Property(nameInDb = "USER_ID") + private Long userId; + + @Property(nameInDb = "SUBJECT") + private String subject = ""; + + @Property(nameInDb = "VALUE") + protected String value = ""; + + @Property(nameInDb = "COLOR") + private String color = ""; + + @Property(nameInDb = "SYMBOL") + private String symbol = ""; + + @Property(nameInDb = "DESCRIPTION") + private String description = ""; + + @Property(nameInDb = "WEIGHT") + private String weight = ""; + + @Property(nameInDb = "DATE") + private String date = ""; + + @Property(nameInDb = "TEACHER") + private String teacher = ""; + + @Property(nameInDb = "SEMESTER") + private String semester = ""; + + @Property(nameInDb = "IS_NEW") + private boolean isNew = false; + + @Property(nameInDb = "READ") + private boolean read = true; + + private static final long serialVersionUID = 42L; + + @Generated(hash = 568899968) + public Grade(Long id, Long subjectId, Long userId, String subject, String value, + String color, String symbol, String description, String weight, + String date, String teacher, String semester, boolean isNew, + boolean read) { + this.id = id; + this.subjectId = subjectId; + this.userId = userId; + this.subject = subject; + this.value = value; + this.color = color; + this.symbol = symbol; + this.description = description; + this.weight = weight; + this.date = date; + this.teacher = teacher; + this.semester = semester; + this.isNew = isNew; + this.read = read; + } + + @Generated(hash = 2042976393) + public Grade() { + } + + /** + * Used to resolve relations + */ + @Generated(hash = 2040040024) + private transient DaoSession daoSession; + + /** + * Used for active entity operations. + */ + @Generated(hash = 681281562) + private transient GradeDao myDao; + + public int getValueColor() { + + String replacedString = value.replaceAll("[^0-9]", ""); + + if (!"".equals(replacedString)) { + switch (Integer.parseInt(replacedString)) { + case 6: + return R.color.six_grade; + case 5: + return R.color.five_grade; + case 4: + return R.color.four_grade; + case 3: + return R.color.three_grade; + case 2: + return R.color.two_grade; + case 1: + return R.color.one_grade; + default: + return R.color.default_grade; + } + } + return R.color.default_grade; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + Grade grade = (Grade) o; + + return new EqualsBuilder() + .append(subject, grade.subject) + .append(value, grade.value) + .append(color, grade.color) + .append(symbol, grade.symbol) + .append(description, grade.description) + .append(weight, grade.weight) + .append(date, grade.date) + .append(teacher, grade.teacher) + .append(semester, grade.semester) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(subject) + .append(value) + .append(color) + .append(symbol) + .append(description) + .append(weight) + .append(date) + .append(teacher) + .append(semester) + .toHashCode(); + } + + public Long getId() { + return id; + } + + public Grade setId(Long id) { + this.id = id; + return this; + } + + public Long getSubjectId() { + return subjectId; + } + + public Grade setSubjectId(Long subjectId) { + this.subjectId = subjectId; + return this; + } + + public Long getUserId() { + return userId; + } + + public Grade setUserId(Long userId) { + this.userId = userId; + return this; + } + + public String getSubject() { + return subject; + } + + public Grade setSubject(String subject) { + this.subject = subject; + return this; + } + + public String getValue() { + return value; + } + + public Grade setValue(String value) { + this.value = value; + return this; + } + + public String getColor() { + return color; + } + + public Grade setColor(String color) { + this.color = color; + return this; + } + + public String getSymbol() { + return symbol; + } + + public Grade setSymbol(String symbol) { + this.symbol = symbol; + return this; + } + + public String getDescription() { + return description; + } + + public Grade setDescription(String description) { + this.description = description; + return this; + } + + public String getWeight() { + return weight; + } + + public Grade setWeight(String weight) { + this.weight = weight; + return this; + } + + public String getDate() { + return date; + } + + public Grade setDate(String date) { + this.date = date; + return this; + } + + public String getTeacher() { + return teacher; + } + + public Grade setTeacher(String teacher) { + this.teacher = teacher; + return this; + } + + public String getSemester() { + return semester; + } + + public Grade setSemester(String semester) { + this.semester = semester; + return this; + } + + public boolean getIsNew() { + return this.isNew; + } + + public Grade setIsNew(boolean isNew) { + this.isNew = isNew; + return this; + } + + public boolean getRead() { + return this.read; + } + + public Grade setRead(boolean read) { + this.read = read; + return this; + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 128553479) + public void delete() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.delete(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 1942392019) + public void refresh() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.refresh(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 713229351) + public void update() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.update(this); + } + + /** called by internal mechanisms, do not call yourself. */ + @Generated(hash = 1187286414) + public void __setDaoSession(DaoSession daoSession) { + this.daoSession = daoSession; + myDao = daoSession != null ? daoSession.getGradeDao() : null; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Subject.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Subject.java new file mode 100644 index 000000000..6bcce6229 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Subject.java @@ -0,0 +1,192 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.DaoException; +import org.greenrobot.greendao.annotation.Entity; +import org.greenrobot.greendao.annotation.Generated; +import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.Property; +import org.greenrobot.greendao.annotation.ToMany; + +import java.util.List; + +@Entity( + nameInDb = "Subjects", + active = true +) +public class Subject { + + @Id(autoincrement = true) + private Long id; + + @Property(nameInDb = "USER_ID") + private Long userId; + + @Property(nameInDb = "NAME") + private String name; + + @Property(nameInDb = "PREDICTED_RATING") + private String predictedRating; + + @Property(nameInDb = "FINAL_RATING") + private String finalRating; + + @Property(nameInDb = "SEMESTER") + private String semester; + + @ToMany(referencedJoinProperty = "subjectId") + private List gradeList; + + /** + * Used to resolve relations + */ + @Generated(hash = 2040040024) + private transient DaoSession daoSession; + + /** + * Used for active entity operations. + */ + @Generated(hash = 1644932788) + private transient SubjectDao myDao; + + @Generated(hash = 396325764) + public Subject(Long id, Long userId, String name, String predictedRating, + String finalRating, String semester) { + this.id = id; + this.userId = userId; + this.name = name; + this.predictedRating = predictedRating; + this.finalRating = finalRating; + this.semester = semester; + } + + @Generated(hash = 1617906264) + public Subject() { + } + + public Long getId() { + return id; + } + + public Subject setId(Long id) { + this.id = id; + return this; + } + + public Long getUserId() { + return userId; + } + + public Subject setUserId(Long userId) { + this.userId = userId; + return this; + } + + public String getName() { + return name; + } + + public Subject setName(String name) { + this.name = name; + return this; + } + + public String getPredictedRating() { + return predictedRating; + } + + public Subject setPredictedRating(String predictedRating) { + this.predictedRating = predictedRating; + return this; + } + + public String getFinalRating() { + return finalRating; + } + + public Subject setFinalRating(String finalRating) { + this.finalRating = finalRating; + return this; + } + + public String getSemester() { + return semester; + } + + public Subject setSemester(String semester) { + this.semester = semester; + return this; + } + + /** + * To-many relationship, resolved on first access (and after reset). + * Changes to to-many relations are not persisted, make changes to the target entity. + */ + @Generated(hash = 1358847893) + public List getGradeList() { + if (gradeList == null) { + final DaoSession daoSession = this.daoSession; + if (daoSession == null) { + throw new DaoException("Entity is detached from DAO context"); + } + GradeDao targetDao = daoSession.getGradeDao(); + List gradeListNew = targetDao._querySubject_GradeList(id); + synchronized (this) { + if (gradeList == null) { + gradeList = gradeListNew; + } + } + } + return gradeList; + } + + /** + * Resets a to-many relationship, making the next get call to query for a fresh result. + */ + @Generated(hash = 1939990047) + public synchronized void resetGradeList() { + gradeList = null; + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 128553479) + public void delete() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.delete(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 1942392019) + public void refresh() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.refresh(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 713229351) + public void update() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.update(this); + } + + /** called by internal mechanisms, do not call yourself. */ + @Generated(hash = 937984622) + public void __setDaoSession(DaoSession daoSession) { + this.daoSession = daoSession; + myDao = daoSession != null ? daoSession.getSubjectDao() : null; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java new file mode 100644 index 000000000..390f679ef --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java @@ -0,0 +1,330 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.DaoException; +import org.greenrobot.greendao.annotation.Entity; +import org.greenrobot.greendao.annotation.Generated; +import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.Index; +import org.greenrobot.greendao.annotation.Property; + +import java.io.Serializable; + +@Entity( + nameInDb = "TimetableLessons", + active = true, + indexes = {@Index(value = "dayId,date,startTime,endTime", unique = true)} +) +public class TimetableLesson implements Serializable { + + @Id(autoincrement = true) + private Long id; + + @Property(nameInDb = "DAY_ID") + private Long dayId; + + @Property(nameInDb = "NUMBER_OF_LESSON") + private String number; + + @Property(nameInDb = "SUBJECT_NAME") + private String subject = ""; + + @Property(nameInDb = "TEACHER") + private String teacher = ""; + + @Property(nameInDb = "ROOM") + private String room = ""; + + @Property(nameInDb = "DESCRIPTION") + private String description = ""; + + @Property(nameInDb = "GROUP_NAME") + private String groupName = ""; + + @Property(nameInDb = "START_TIME") + private String startTime = ""; + + @Property(nameInDb = "END_TIME") + private String endTime = ""; + + @Property(nameInDb = "DATE") + private String date = ""; + + @Property(nameInDb = "IS_EMPTY") + private boolean isEmpty = false; + + @Property(nameInDb = "IS_DIVISION_INTO_GROUP") + private boolean isDivisionIntoGroups = false; + + @Property(nameInDb = "IS_PLANNING") + private boolean isPlanning = false; + + @Property(nameInDb = "IS_REALIZED") + private boolean isRealized = false; + + @Property(nameInDb = "IS_MOVED_CANCELED") + private boolean isMovedOrCanceled = false; + + @Property(nameInDb = "IS_NEW_MOVED_IN_CANCELED") + private boolean isNewMovedInOrChanged = false; + + private static final long serialVersionUID = 42L; + + /** + * Used to resolve relations + */ + @Generated(hash = 2040040024) + private transient DaoSession daoSession; + + /** + * Used for active entity operations. + */ + @Generated(hash = 1119360138) + private transient TimetableLessonDao myDao; + + @Generated(hash = 627457324) + public TimetableLesson(Long id, Long dayId, String number, String subject, + String teacher, String room, String description, String groupName, + String startTime, String endTime, String date, boolean isEmpty, + boolean isDivisionIntoGroups, boolean isPlanning, boolean isRealized, + boolean isMovedOrCanceled, boolean isNewMovedInOrChanged) { + this.id = id; + this.dayId = dayId; + this.number = number; + this.subject = subject; + this.teacher = teacher; + this.room = room; + this.description = description; + this.groupName = groupName; + this.startTime = startTime; + this.endTime = endTime; + this.date = date; + this.isEmpty = isEmpty; + this.isDivisionIntoGroups = isDivisionIntoGroups; + this.isPlanning = isPlanning; + this.isRealized = isRealized; + this.isMovedOrCanceled = isMovedOrCanceled; + this.isNewMovedInOrChanged = isNewMovedInOrChanged; + } + + @Generated(hash = 1878030142) + public TimetableLesson() { + } + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getDayId() { + return this.dayId; + } + + public void setDayId(Long dayId) { + this.dayId = dayId; + } + + public String getNumber() { + return this.number; + } + + public TimetableLesson setNumber(String number) { + this.number = number; + return this; + } + + public String getSubject() { + return this.subject; + } + + public TimetableLesson setSubject(String subject) { + this.subject = subject; + return this; + } + + public String getTeacher() { + return this.teacher; + } + + public TimetableLesson setTeacher(String teacher) { + this.teacher = teacher; + return this; + } + + public String getRoom() { + return this.room; + } + + public TimetableLesson setRoom(String room) { + this.room = room; + return this; + } + + public String getDescription() { + return this.description; + } + + public TimetableLesson setDescription(String description) { + this.description = description; + return this; + } + + public String getGroupName() { + return this.groupName; + } + + public TimetableLesson setGroupName(String groupName) { + this.groupName = groupName; + return this; + } + + public String getStartTime() { + return this.startTime; + } + + public TimetableLesson setStartTime(String startTime) { + this.startTime = startTime; + return this; + } + + public String getEndTime() { + return this.endTime; + } + + public TimetableLesson setEndTime(String endTime) { + this.endTime = endTime; + return this; + } + + public String getDate() { + return this.date; + } + + public TimetableLesson setDate(String date) { + this.date = date; + return this; + } + + public boolean getIsEmpty() { + return this.isEmpty; + } + + public TimetableLesson setEmpty(boolean isEmpty) { + this.isEmpty = isEmpty; + return this; + } + + public boolean getIsDivisionIntoGroups() { + return this.isDivisionIntoGroups; + } + + public TimetableLesson setDivisionIntoGroups(boolean isDivisionIntoGroups) { + this.isDivisionIntoGroups = isDivisionIntoGroups; + return this; + } + + public boolean getIsPlanning() { + return this.isPlanning; + } + + public TimetableLesson setPlanning(boolean isPlanning) { + this.isPlanning = isPlanning; + return this; + } + + public boolean getIsRealized() { + return this.isRealized; + } + + public TimetableLesson setRealized(boolean isRealized) { + this.isRealized = isRealized; + return this; + } + + public boolean getIsMovedOrCanceled() { + return this.isMovedOrCanceled; + } + + public TimetableLesson setMovedOrCanceled(boolean isMovedOrCanceled) { + this.isMovedOrCanceled = isMovedOrCanceled; + return this; + } + + public boolean getIsNewMovedInOrChanged() { + return this.isNewMovedInOrChanged; + } + + public TimetableLesson setNewMovedInOrChanged(boolean isNewMovedInOrChanged) { + this.isNewMovedInOrChanged = isNewMovedInOrChanged; + return this; + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 128553479) + public void delete() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.delete(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 1942392019) + public void refresh() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.refresh(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 713229351) + public void update() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.update(this); + } + + public void setIsEmpty(boolean isEmpty) { + this.isEmpty = isEmpty; + } + + public void setIsDivisionIntoGroups(boolean isDivisionIntoGroups) { + this.isDivisionIntoGroups = isDivisionIntoGroups; + } + + public void setIsPlanning(boolean isPlanning) { + this.isPlanning = isPlanning; + } + + public void setIsRealized(boolean isRealized) { + this.isRealized = isRealized; + } + + public void setIsMovedOrCanceled(boolean isMovedOrCanceled) { + this.isMovedOrCanceled = isMovedOrCanceled; + } + + public void setIsNewMovedInOrChanged(boolean isNewMovedInOrChanged) { + this.isNewMovedInOrChanged = isNewMovedInOrChanged; + } + + /** called by internal mechanisms, do not call yourself. */ + @Generated(hash = 1885258429) + public void __setDaoSession(DaoSession daoSession) { + this.daoSession = daoSession; + myDao = daoSession != null ? daoSession.getTimetableLessonDao() : null; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java new file mode 100644 index 000000000..778f6ca3c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java @@ -0,0 +1,173 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.DaoException; +import org.greenrobot.greendao.annotation.Entity; +import org.greenrobot.greendao.annotation.Generated; +import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.Index; +import org.greenrobot.greendao.annotation.Property; +import org.greenrobot.greendao.annotation.ToMany; + +import java.util.List; + +@Entity( + nameInDb = "Weeks", + active = true, + indexes ={@Index(value = "userId,startDayDate", unique = true)} +) +public class Week { + + @Id(autoincrement = true) + private Long id; + + @Property(nameInDb = "USER_ID") + private Long userId; + + @Property(nameInDb = "START_DATE") + private String startDayDate = ""; + + @Property(nameInDb = "IS_ATTENDANCE_SYNCED") + private boolean isAttendanceSynced = false; + + @Property(nameInDb = "IS_TIMETABLE_SYNCED") + private boolean isTimetableSynced = false; + + @ToMany(referencedJoinProperty = "weekId") + private List dayList; + + /** Used to resolve relations */ + @Generated(hash = 2040040024) + private transient DaoSession daoSession; + + /** Used for active entity operations. */ + @Generated(hash = 1019310398) + private transient WeekDao myDao; + + @Generated(hash = 1745118398) + public Week(Long id, Long userId, String startDayDate, boolean isAttendanceSynced, + boolean isTimetableSynced) { + this.id = id; + this.userId = userId; + this.startDayDate = startDayDate; + this.isAttendanceSynced = isAttendanceSynced; + this.isTimetableSynced = isTimetableSynced; + } + + @Generated(hash = 2135529658) + public Week() { + } + + public Long getId() { + return id; + } + + public Week setId(Long id) { + this.id = id; + return this; + } + + public Long getUserId() { + return userId; + } + + public Week setUserId(Long userId) { + this.userId = userId; + return this; + } + + public String getStartDayDate() { + return startDayDate; + } + + public Week setStartDayDate(String startDayDate) { + this.startDayDate = startDayDate; + return this; + } + + public boolean getIsAttendanceSynced() { + return this.isAttendanceSynced; + } + + public void setIsAttendanceSynced(boolean isAttendanceSynced) { + this.isAttendanceSynced = isAttendanceSynced; + } + + public boolean getIsTimetableSynced() { + return this.isTimetableSynced; + } + + public void setIsTimetableSynced(boolean isTimetableSynced) { + this.isTimetableSynced = isTimetableSynced; + } + + /** + * To-many relationship, resolved on first access (and after reset). + * Changes to to-many relations are not persisted, make changes to the target entity. + */ + @Generated(hash = 1562119145) + public List getDayList() { + if (dayList == null) { + final DaoSession daoSession = this.daoSession; + if (daoSession == null) { + throw new DaoException("Entity is detached from DAO context"); + } + DayDao targetDao = daoSession.getDayDao(); + List dayListNew = targetDao._queryWeek_DayList(id); + synchronized (this) { + if (dayList == null) { + dayList = dayListNew; + } + } + } + return dayList; + } + + /** Resets a to-many relationship, making the next get call to query for a fresh result. */ + @Generated(hash = 1010399236) + public synchronized void resetDayList() { + dayList = null; + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 128553479) + public void delete() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.delete(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 1942392019) + public void refresh() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.refresh(this); + } + + /** + * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. + * Entity must attached to an entity context. + */ + @Generated(hash = 713229351) + public void update() { + if (myDao == null) { + throw new DaoException("Entity is detached from DAO context"); + } + myDao.update(this); + } + + /** called by internal mechanisms, do not call yourself. */ + @Generated(hash = 665278367) + public void __setDaoSession(DaoSession daoSession) { + this.daoSession = daoSession; + myDao = daoSession != null ? daoSession.getWeekDao() : null; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt deleted file mode 100644 index 3c58971ae..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "Attendance") -data class Attendance( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "diary_id") - val diaryId: Int, - - val date: LocalDate, - - val number: Int, - - val subject: String, - - val name: String, - - val presence: Boolean, - - val absence: Boolean, - - val exemption: Boolean, - - val lateness: Boolean, - - val excused: Boolean, - - val deleted: Boolean -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt deleted file mode 100644 index d2e1f174e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.Month -import java.io.Serializable - -@Entity(tableName = "AttendanceSummary") -data class AttendanceSummary( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "diary_id") - val diaryId: Int, - - @ColumnInfo(name = "subject_id") - val subjectId: Int, - - val month: Month, - - val presence: Int, - - val absence: Int, - - @ColumnInfo(name = "absence_excused") - val absenceExcused: Int, - - @ColumnInfo(name = "absence_for_school_reasons") - val absenceForSchoolReasons: Int, - - val lateness: Int, - - @ColumnInfo(name = "lateness_excused") - val latenessExcused: Int, - - val exemption: Int -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt deleted file mode 100644 index 775f3f558..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "CompletedLesson") -data class CompletedLesson( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "diary_id") - val diaryId: Int, - - val date: LocalDate, - - val number: Int, - - val subject: String, - - val topic: String, - - val teacher: String, - - @ColumnInfo(name = "teacher_symbol") - val teacherSymbol: String, - - val substitution: String, - - val absence: String, - - val resources: String -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt deleted file mode 100644 index 9ae795e71..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "Exams") -data class Exam( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "diary_id") - val diaryId: Int, - - val date: LocalDate, - - @ColumnInfo(name = "entry_date") - val entryDate: LocalDate, - - val subject: String, - - val group: String, - - val type: String, - - val description: String, - - val teacher: String, - - @ColumnInfo(name = "teacher_symbol") - val teacherSymbol: String -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt deleted file mode 100644 index 1221a7aab..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "Grades") -data class Grade( - - @ColumnInfo(name = "semester_id") - val semesterId: Int, - - @ColumnInfo(name = "student_id") - val studentId: Int, - - val subject: String, - - val entry: String, - - val value: Int, - - val modifier: Double, - - val comment: String, - - val color: String, - - @ColumnInfo(name = "grade_symbol") - val gradeSymbol: String, - - val description: String, - - val weight: String, - - val weightValue: Double, - - val date: LocalDate, - - val teacher: String -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 - - @ColumnInfo(name = "is_read") - var isRead: Boolean = true - - @ColumnInfo(name = "is_notified") - var isNotified: Boolean = true -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt deleted file mode 100644 index 8ad8b8b8d..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity(tableName = "GradesStatistics") -data class GradeStatistics( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "semester_id") - val semesterId: Int, - - val subject: String, - - val grade: Int, - - val amount: Int, - - @ColumnInfo(name = "is_semester") - val semester: Boolean -) { - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt deleted file mode 100644 index e6ac4926d..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity(tableName = "GradesSummary") -data class GradeSummary( - - @ColumnInfo(name = "semester_id") - val semesterId: Int, - - @ColumnInfo(name = "student_id") - val studentId: Int, - - val subject: String, - - val predictedGrade: String, - - val finalGrade: String -) { - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt deleted file mode 100644 index a22df0961..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "Homework") -data class Homework( - - @ColumnInfo(name = "semester_id") - val semesterId: Int, - - @ColumnInfo(name = "student_id") - val studentId: Int, - - val date: LocalDate, - - @ColumnInfo(name = "entry_date") - val entryDate: LocalDate, - - val subject: String, - - val content: String, - - val teacher: String, - - @ColumnInfo(name = "teacher_symbol") - val teacherSymbol: String - -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt deleted file mode 100644 index 5b9130f5d..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "LuckyNumbers") -data class LuckyNumber ( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - val date: LocalDate, - - @ColumnInfo(name = "lucky_number") - val luckyNumber: Int - -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 - - @ColumnInfo(name = "is_notified") - var isNotified: Boolean = true -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt deleted file mode 100644 index 48b4fd022..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDateTime -import java.io.Serializable - -@Entity(tableName = "Messages") -data class Message( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "real_id") - val realId: Int, - - @ColumnInfo(name = "message_id") - val messageId: Int, - - @ColumnInfo(name = "sender_name") - val sender: String, - - @ColumnInfo(name = "sender_id") - val senderId: Int, - - @ColumnInfo(name = "recipient_name") - val recipient: String, - - val subject: String, - - val date: LocalDateTime, - - @ColumnInfo(name = "folder_id") - val folderId: Int, - - var unread: Boolean, - - @ColumnInfo(name = "unread_by") - val unreadBy: Int, - - @ColumnInfo(name = "read_by") - val readBy: Int, - - val removed: Boolean -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 - - @ColumnInfo(name = "is_notified") - var isNotified: Boolean = true - - var content: String? = null -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt deleted file mode 100644 index 5f3a92ab6..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "Notes") -data class Note( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - val date: LocalDate, - - val teacher: String, - - val category: String, - - val content: String -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 - - @ColumnInfo(name = "is_read") - var isRead: Boolean = true - - @ColumnInfo(name = "is_notified") - var isNotified: Boolean = true -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt deleted file mode 100644 index 3021da72d..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import java.io.Serializable - -@Entity(tableName = "Recipients") -data class Recipient( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "real_id") - val realId: String, - - val name: String, - - @ColumnInfo(name = "real_name") - val realName: String, - - @ColumnInfo(name = "login_id") - val loginId: Int, - - @ColumnInfo(name = "unit_id") - val unitId: Int, - - val role: Int, - - val hash: String - -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 - - override fun toString() = name -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt deleted file mode 100644 index 601d8aac7..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import java.io.Serializable - -@Entity(tableName = "ReportingUnits") -data class ReportingUnit( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "real_id") - val realId: Int, - - @ColumnInfo(name = "short") - val shortName: String, - - @ColumnInfo(name = "sender_id") - val senderId: Int, - - @ColumnInfo(name = "sender_name") - val senderName: String, - - val roles: List - -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt deleted file mode 100644 index 0f44fa2d9..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.Index -import androidx.room.PrimaryKey - -@Entity(tableName = "Semesters", indices = [Index(value = ["student_id", "diary_id", "semester_id"], unique = true)]) -data class Semester( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "diary_id") - val diaryId: Int, - - @ColumnInfo(name = "diary_name") - val diaryName: String, - - @ColumnInfo(name = "semester_id") - val semesterId: Int, - - @ColumnInfo(name = "semester_name") - val semesterName: Int, - - @ColumnInfo(name = "is_current") - val isCurrent: Boolean, - - @ColumnInfo(name = "class_id") - val classId: Int, - - @ColumnInfo(name = "unit_id") - val unitId: Int -) { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt deleted file mode 100644 index 9cacf8358..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.Index -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDateTime -import java.io.Serializable - -@Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id", "class_id"], unique = true)]) -data class Student( - - val endpoint: String, - - val loginType: String, - - val email: String, - - var password: String, - - val symbol: String, - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "student_name") - val studentName: String, - - @ColumnInfo(name = "school_id") - val schoolSymbol: String, - - @ColumnInfo(name = "school_name") - val schoolName: String, - - @ColumnInfo(name = "class_id") - val classId: Int, - - @ColumnInfo(name = "is_current") - val isCurrent: Boolean, - - @ColumnInfo(name = "registration_date") - val registrationDate: LocalDateTime -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt deleted file mode 100644 index dbaa6f4ed..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import java.io.Serializable - -@Entity(tableName = "Subjects") -data class Subject( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "diary_id") - val diaryId: Int, - - @ColumnInfo(name = "real_id") - val realId: Int, - - val name: String -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt deleted file mode 100644 index 9bc3d2140..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDateTime -import java.io.Serializable - -@Entity(tableName = "Timetable") -data class Timetable( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "diary_id") - val diaryId: Int, - - val number: Int, - - val start: LocalDateTime, - - val end: LocalDateTime, - - val date: LocalDate, - - val subject: String, - - val subjectOld: String, - - val group: String, - - val room: String, - - val roomOld: String, - - val teacher: String, - - val teacherOld: String, - - val info: String, - - val changes: Boolean, - - val canceled: Boolean -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt deleted file mode 100644 index c26a02d1f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration10 : Migration(9, 10) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE Grades_Summary RENAME TO GradesSummary") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt deleted file mode 100644 index cb437c0ee..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration11 : Migration(10, 11) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL(""" - CREATE TABLE IF NOT EXISTS Grades_temp ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - is_read INTEGER NOT NULL, - is_notified INTEGER NOT NULL, - semester_id INTEGER NOT NULL, - student_id INTEGER NOT NULL, - subject TEXT NOT NULL, - entry TEXT NOT NULL, - value INTEGER NOT NULL, - modifier REAL NOT NULL, - comment TEXT NOT NULL, - color TEXT NOT NULL, - grade_symbol TEXT NOT NULL, - description TEXT NOT NULL, - weight TEXT NOT NULL, - weightValue REAL NOT NULL, - date INTEGER NOT NULL, - teacher TEXT NOT NULL - ) - """) - database.execSQL("INSERT INTO Grades_temp SELECT * FROM Grades") - database.execSQL("DROP TABLE Grades") - database.execSQL("ALTER TABLE Grades_temp RENAME TO Grades") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt deleted file mode 100644 index 1dc38e14c..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt +++ /dev/null @@ -1,69 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration12 : Migration(11, 12) { - - override fun migrate(database: SupportSQLiteDatabase) { - createTempStudentsTable(database) - replaceStudentTable(database) - updateStudentsWithClassId(database, getStudentsIds(database)) - removeStudentsWithNoClassId(database) - ensureThereIsOnlyOneCurrentStudent(database) - } - - private fun createTempStudentsTable(database: SupportSQLiteDatabase) { - database.execSQL(""" - CREATE TABLE IF NOT EXISTS Students_tmp ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - endpoint TEXT NOT NULL, - loginType TEXT NOT NULL, - email TEXT NOT NULL, - password TEXT NOT NULL, - symbol TEXT NOT NULL, - student_id INTEGER NOT NULL, - student_name TEXT NOT NULL, - school_id TEXT NOT NULL, - school_name TEXT NOT NULL, - is_current INTEGER NOT NULL, - registration_date INTEGER NOT NULL, - class_id INTEGER NOT NULL - ) - """) - database.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students_tmp (email, symbol, student_id, school_id, class_id)") - } - - private fun replaceStudentTable(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE Students ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") - database.execSQL("INSERT INTO Students_tmp SELECT * FROM Students") - database.execSQL("DROP TABLE Students") - database.execSQL("ALTER TABLE Students_tmp RENAME TO Students") - } - - private fun getStudentsIds(database: SupportSQLiteDatabase): List { - val students = mutableListOf() - val studentsCursor = database.query("SELECT student_id FROM Students") - if (studentsCursor.moveToFirst()) { - do { - students.add(studentsCursor.getInt(0)) - } while (studentsCursor.moveToNext()) - } - return students - } - - private fun updateStudentsWithClassId(database: SupportSQLiteDatabase, students: List) { - students.forEach { - database.execSQL("UPDATE Students SET class_id = IFNULL((SELECT class_id FROM Semesters WHERE student_id = $it), 0) WHERE student_id = $it") - } - } - - private fun removeStudentsWithNoClassId(database: SupportSQLiteDatabase) { - database.execSQL("DELETE FROM Students WHERE class_id = 0") - } - - private fun ensureThereIsOnlyOneCurrentStudent(database: SupportSQLiteDatabase) { - database.execSQL("UPDATE Students SET is_current = 0") - database.execSQL("UPDATE Students SET is_current = 1 WHERE id = (SELECT MAX(id) FROM Students)") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt deleted file mode 100644 index c5a30991a..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration2 : Migration(1, 2) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL(""" - CREATE TABLE IF NOT EXISTS LuckyNumbers ( - id INTEGER PRIMARY KEY NOT NULL, - is_notified INTEGER NOT NULL, - student_id INTEGER NOT NULL, - date INTEGER NOT NULL, - lucky_number INTEGER NOT NULL) - """) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt deleted file mode 100644 index d9699c0f4..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration3 : Migration(2, 3) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL(""" - CREATE TABLE IF NOT EXISTS CompletedLesson ( - id INTEGER PRIMARY KEY NOT NULL, - student_id INTEGER NOT NULL, - diary_id INTEGER NOT NULL, - date INTEGER NOT NULL, - number INTEGER NOT NULL, - subject TEXT NOT NULL, - topic TEXT NOT NULL, - teacher TEXT NOT NULL, - teacher_symbol TEXT NOT NULL, - substitution TEXT NOT NULL, - absence TEXT NOT NULL, - resources TEXT NOT NULL) - """) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt deleted file mode 100644 index 0ae89bdd6..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration4 : Migration(3, 4) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS Messages") - database.execSQL(""" - CREATE TABLE IF NOT EXISTS Messages ( - id INTEGER PRIMARY KEY NOT NULL, - is_notified INTEGER NOT NULL, - content TEXT, - student_id INTEGER NOT NULL, - real_id INTEGER NOT NULL, - message_id INTEGER NOT NULL, - sender_name TEXT NOT NULL, - sender_id INTEGER NOT NULL, - recipient_id INTEGER NOT NULL, - recipient_name TEXT NOT NULL, - subject TEXT NOT NULL, - date INTEGER NOT NULL, - folder_id INTEGER NOT NULL, - unread INTEGER NOT NULL, - unreadBy INTEGER NOT NULL, - readBy INTEGER NOT NULL, - removed INTEGER NOT NULL) - """) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt deleted file mode 100644 index fe0dec48f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase -import org.threeten.bp.LocalDateTime.now -import org.threeten.bp.ZoneOffset - -class Migration5 : Migration(4, 5) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE Students ADD COLUMN registration_date INTEGER DEFAULT 0 NOT NULL") - database.execSQL("UPDATE Students SET registration_date = '${now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli()}'") - database.execSQL("DROP TABLE IF EXISTS Notes") - database.execSQL(""" - CREATE TABLE IF NOT EXISTS Notes ( - id INTEGER PRIMARY KEY NOT NULL, - is_read INTEGER NOT NULL, - is_notified INTEGER NOT NULL, - student_id INTEGER NOT NULL, - date INTEGER NOT NULL, - teacher TEXT NOT NULL, - category TEXT NOT NULL, - content TEXT NOT NULL) - """) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt deleted file mode 100644 index fa9436187..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration6 : Migration(5, 6) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL(""" - CREATE TABLE IF NOT EXISTS ReportingUnits ( - id INTEGER PRIMARY KEY NOT NULL, - student_id INTEGER NOT NULL, - real_id INTEGER NOT NULL, - short TEXT NOT NULL, - sender_id INTEGER NOT NULL, - sender_name TEXT NOT NULL, - roles TEXT NOT NULL) - """) - - database.execSQL(""" - CREATE TABLE IF NOT EXISTS Recipients ( - id INTEGER PRIMARY KEY NOT NULL, - student_id INTEGER NOT NULL, - real_id TEXT NOT NULL, - name TEXT NOT NULL, - real_name TEXT NOT NULL, - login_id INTEGER NOT NULL, - unit_id INTEGER NOT NULL, - role INTEGER NOT NULL, - hash TEXT NOT NULL) - """) - - database.execSQL("DELETE FROM Semesters WHERE 1") - database.execSQL("ALTER TABLE Semesters ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") - database.execSQL("ALTER TABLE Semesters ADD COLUMN unit_id INTEGER DEFAULT 0 NOT NULL") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt deleted file mode 100644 index 120716c81..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration7 : Migration(6, 7) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL(""" - CREATE TABLE IF NOT EXISTS GradesStatistics ( - id INTEGER PRIMARY KEY NOT NULL, - student_id INTEGER NOT NULL, - semester_id INTEGER NOT NULL, - subject TEXT NOT NULL, - grade INTEGER NOT NULL, - amount INTEGER NOT NULL, - is_semester INTEGER NOT NULL) - """) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt deleted file mode 100644 index 7009ee129..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration8 : Migration(7, 8) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE Timetable ADD COLUMN subjectOld TEXT DEFAULT \"\" NOT NULL") - database.execSQL("ALTER TABLE Timetable ADD COLUMN roomOld TEXT DEFAULT \"\" NOT NULL") - database.execSQL("ALTER TABLE Timetable ADD COLUMN teacherOld TEXT DEFAULT \"\" NOT NULL") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt deleted file mode 100644 index d79a57062..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration9 : Migration(8, 9) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS Messages") - database.execSQL(""" - CREATE TABLE IF NOT EXISTS Messages ( - id INTEGER PRIMARY KEY NOT NULL, - student_id INTEGER NOT NULL, - real_id INTEGER NOT NULL, - message_id INTEGER NOT NULL, - sender_name TEXT NOT NULL, - sender_id INTEGER NOT NULL, - recipient_name TEXT NOT NULL, - subject TEXT NOT NULL, - date INTEGER NOT NULL, - folder_id INTEGER NOT NULL, - unread INTEGER NOT NULL, - unread_by INTEGER NOT NULL, - read_by INTEGER NOT NULL, - removed INTEGER NOT NULL, - is_notified INTEGER NOT NULL, - content TEXT) - """) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java b/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java new file mode 100644 index 000000000..349ac177e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java @@ -0,0 +1,91 @@ +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.NotLoggedInErrorException; +import io.github.wulkanowy.api.VulcanOfflineException; +import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; +import io.github.wulkanowy.di.annotations.ApplicationContext; +import io.github.wulkanowy.utils.AppConstant; +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(); + } + } + + @Override + public String getAttendanceLessonDescription(AttendanceLesson lesson) { + int id = R.string.attendance_present; + + if (lesson.getIsAbsenceForSchoolReasons()) { + id = R.string.attendance_absence_for_school_reasons; + } + + if (lesson.getIsAbsenceExcused()) { + id = R.string.attendance_absence_excused; + } + + if (lesson.getIsAbsenceUnexcused()) { + id = R.string.attendance_absence_unexcused; + } + + if (lesson.getIsExemption()) { + id = R.string.attendance_exemption; + } + + if (lesson.getIsExcusedLateness()) { + id = R.string.attendance_excused_lateness; + } + + if (lesson.getIsUnexcusedLateness()) { + id = R.string.attendance_unexcused_lateness; + } + + return resources.getString(id); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java new file mode 100644 index 000000000..0ef45a76d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java @@ -0,0 +1,14 @@ +package io.github.wulkanowy.data.db.resources; + +import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; + +public interface ResourcesContract { + + String[] getSymbolsKeysArray(); + + String[] getSymbolsValuesArray(); + + String getErrorLoginMessage(Exception e); + + String getAttendanceLessonDescription(AttendanceLesson lesson); +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPref.java b/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPref.java new file mode 100644 index 000000000..ba437f882 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPref.java @@ -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(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefContract.java b/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefContract.java new file mode 100644 index 000000000..7f540acfd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefContract.java @@ -0,0 +1,8 @@ +package io.github.wulkanowy.data.db.shared; + +public interface SharedPrefContract { + + long getCurrentUserId(); + + void setCurrentUserId(long userId); +} diff --git a/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt b/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt deleted file mode 100644 index 58a2396e2..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt +++ /dev/null @@ -1,3 +0,0 @@ -package io.github.wulkanowy.data.exceptions - -class NoCurrentStudentException : Exception("There no set current student in database") diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt deleted file mode 100644 index 0f5873766..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendance - -import io.github.wulkanowy.data.db.dao.AttendanceDao -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDao) { - - fun saveAttendance(attendance: List) { - attendanceDb.insertAll(attendance) - } - - fun deleteAttendance(attendance: List) { - attendanceDb.deleteAll(attendance) - } - - fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { - return attendanceDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt deleted file mode 100644 index b3544c3f5..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendance - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate -import io.reactivex.Single -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceRemote @Inject constructor(private val api: Api) { - - fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getAttendance(startDate, endDate) }.map { attendance -> - attendance.map { - Attendance( - studentId = semester.studentId, - diaryId = semester.diaryId, - date = it.date.toLocalDate(), - number = it.number, - subject = it.subject, - name = it.name, - presence = it.presence, - absence = it.absence, - exemption = it.exemption, - lateness = it.lateness, - excused = it.excused, - deleted = it.deleted - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt deleted file mode 100644 index f6eb07dae..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendance - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Single -import org.threeten.bp.LocalDate -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: AttendanceLocal, - private val remote: AttendanceRemote -) { - - fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean) - : Single> { - return Single.fromCallable { startDate.monday to endDate.friday } - .flatMap { dates -> - local.getAttendance(semester, dates.first, dates.second).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap { - if (it) remote.getAttendance(semester, dates.first, dates.second) - else Single.error(UnknownHostException()) - }.flatMap { newAttendance -> - local.getAttendance(semester, dates.first, dates.second) - .toSingle(emptyList()) - .doOnSuccess { oldAttendance -> - local.deleteAttendance(oldAttendance - newAttendance) - local.saveAttendance(newAttendance - oldAttendance) - } - }.flatMap { - local.getAttendance(semester, dates.first, dates.second) - .toSingle(emptyList()) - }).map { list -> list.filter { it.date in startDate..endDate } } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryLocal.kt deleted file mode 100644 index 2e9a10067..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryLocal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendancesummary - -import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceSummaryLocal @Inject constructor(private val attendanceDb: AttendanceSummaryDao) { - - fun saveAttendanceSummary(attendance: List) { - attendanceDb.insertAll(attendance) - } - - fun deleteAttendanceSummary(attendance: List) { - attendanceDb.deleteAll(attendance) - } - - fun getAttendanceSummary(semester: Semester, subjectId: Int): Maybe> { - return attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt deleted file mode 100644 index d38dd3a4b..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendancesummary - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceSummaryRemote @Inject constructor(private val api: Api) { - - fun getAttendanceSummary(semester: Semester, subjectId: Int): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { api.getAttendanceSummary(subjectId) }.map { attendance -> - attendance.map { - AttendanceSummary( - studentId = semester.studentId, - diaryId = semester.diaryId, - subjectId = subjectId, - month = it.month, - presence = it.presence, - absence = it.absence, - absenceExcused = it.absenceExcused, - absenceForSchoolReasons = it.absenceForSchoolReasons, - lateness = it.lateness, - latenessExcused = it.latenessExcused, - exemption = it.exemption - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt deleted file mode 100644 index 90d39aab2..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendancesummary - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Single -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceSummaryRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: AttendanceSummaryLocal, - private val remote: AttendanceSummaryRemote -) { - - fun getAttendanceSummary(semester: Semester, subjectId: Int, forceRefresh: Boolean = false): Single> { - return local.getAttendanceSummary(semester, subjectId).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getAttendanceSummary(semester, subjectId) - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getAttendanceSummary(semester, subjectId).toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteAttendanceSummary(old - new) - local.saveAttendanceSummary(new - old) - } - }.flatMap { local.getAttendanceSummary(semester, subjectId).toSingle(emptyList()) }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocal.kt deleted file mode 100644 index 9b275908e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.completedlessons - -import io.github.wulkanowy.data.db.dao.CompletedLessonsDao -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class CompletedLessonsLocal @Inject constructor(private val completedLessonsDb: CompletedLessonsDao) { - - fun saveCompletedLessons(completedLessons: List) { - completedLessonsDb.insertAll(completedLessons) - } - - fun deleteCompleteLessons(completedLessons: List) { - completedLessonsDb.deleteAll(completedLessons) - } - - fun getCompletedLessons(semester: Semester, start: LocalDate, end: LocalDate): Maybe> { - return completedLessonsDb.loadAll(semester.diaryId, semester.studentId, start, end).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt deleted file mode 100644 index 58dd5a9d1..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.data.repositories.completedlessons - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.api.toLocalDate -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Single -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class CompletedLessonsRemote @Inject constructor(private val api: Api) { - - fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getCompletedLessons(startDate, endDate) } - .map { lessons -> - lessons.map { - it.absence - CompletedLesson( - studentId = semester.studentId, - diaryId = semester.diaryId, - date = it.date.toLocalDate(), - number = it.number, - subject = it.subject, - topic = it.topic, - teacher = it.teacher, - teacherSymbol = it.teacherSymbol, - substitution = it.substitution, - absence = it.absence, - resources = it.resources - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt deleted file mode 100644 index 5b5941076..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.wulkanowy.data.repositories.completedlessons - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Single -import org.threeten.bp.LocalDate -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class CompletedLessonsRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: CompletedLessonsLocal, - private val remote: CompletedLessonsRemote -) { - - fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single> { - return Single.fromCallable { startDate.monday to endDate.friday } - .flatMap { dates -> - local.getCompletedLessons(semester, dates.first, dates.second).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getCompletedLessons(semester, dates.first, dates.second) - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getCompletedLessons(semester, dates.first, dates.second) - .toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteCompleteLessons(old - new) - local.saveCompletedLessons(new - old) - } - }.flatMap { - local.getCompletedLessons(semester, dates.first, dates.second) - .toSingle(emptyList()) - }).map { list -> list.filter { it.date in startDate..endDate } } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamLocal.kt deleted file mode 100644 index 0f484d323..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamLocal.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.repositories.exam - -import io.github.wulkanowy.data.db.dao.ExamDao -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ExamLocal @Inject constructor(private val examDb: ExamDao) { - - fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { - return examDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate) - .filter { !it.isEmpty() } - } - - fun saveExams(exams: List) { - examDb.insertAll(exams) - } - - fun deleteExams(exams: List) { - examDb.deleteAll(exams) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt deleted file mode 100644 index f6d653a61..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.data.repositories.exam - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate -import io.reactivex.Single -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ExamRemote @Inject constructor(private val api: Api) { - - fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getExams(startDate, endDate) }.map { exams -> - exams.map { - Exam( - studentId = semester.studentId, - diaryId = semester.diaryId, - date = it.date.toLocalDate(), - entryDate = it.entryDate.toLocalDate(), - subject = it.subject, - group = it.group, - type = it.type, - description = it.description, - teacher = it.teacher, - teacherSymbol = it.teacherSymbol - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt deleted file mode 100644 index a0cb5ba11..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.wulkanowy.data.repositories.exam - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Single -import org.threeten.bp.LocalDate -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ExamRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: ExamLocal, - private val remote: ExamRemote -) { - - fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single> { - return Single.fromCallable { startDate.monday to endDate.friday } - .flatMap { dates -> - local.getExams(semester, dates.first, dates.second).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getExams(semester, dates.first, dates.second) - else Single.error(UnknownHostException()) - }.flatMap { newExams -> - local.getExams(semester, dates.first, dates.second) - .toSingle(emptyList()) - .doOnSuccess { oldExams -> - local.deleteExams(oldExams - newExams) - local.saveExams(newExams - oldExams) - } - }.flatMap { - local.getExams(semester, dates.first, dates.second) - .toSingle(emptyList()) - }).map { list -> list.filter { it.date in startDate..endDate } } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeLocal.kt deleted file mode 100644 index 4983a4740..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeLocal.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import io.github.wulkanowy.data.db.dao.GradeDao -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeLocal @Inject constructor(private val gradeDb: GradeDao) { - - fun saveGrades(grades: List) { - gradeDb.insertAll(grades) - } - - fun deleteGrades(grades: List) { - gradeDb.deleteAll(grades) - } - - fun updateGrades(grades: List) { - gradeDb.updateAll(grades) - } - - fun getGrades(semester: Semester): Maybe> { - return gradeDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt deleted file mode 100644 index 570ab7a77..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeRemote @Inject constructor(private val api: Api) { - - fun getGrades(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getGrades(semester.semesterId) } - .map { grades -> - grades.map { - Grade( - semesterId = semester.semesterId, - studentId = semester.studentId, - subject = it.subject, - entry = it.entry, - value = it.value, - modifier = it.modifier, - comment = it.comment, - color = it.color, - gradeSymbol = it.symbol.orEmpty(), - description = it.description.orEmpty(), - weight = it.weight, - weightValue = it.weightValue, - date = it.date.toLocalDate(), - teacher = it.teacher - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt deleted file mode 100644 index 3dd456bff..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Completable -import io.reactivex.Single -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: GradeLocal, - private val remote: GradeRemote -) { - - fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Single> { - return local.getGrades(semester).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getGrades(semester) - else Single.error(UnknownHostException()) - }.flatMap { newGrades -> - local.getGrades(semester).toSingle(emptyList()) - .doOnSuccess { oldGrades -> - val notifyBreakDate = oldGrades.maxBy { it.date }?.date - ?: student.registrationDate.toLocalDate() - local.deleteGrades(oldGrades - newGrades) - local.saveGrades((newGrades - oldGrades) - .onEach { - if (it.date >= notifyBreakDate) it.apply { - isRead = false - if (notify) isNotified = false - } - }) - } - }.flatMap { local.getGrades(semester).toSingle(emptyList()) }) - } - - fun getUnreadGrades(semester: Semester): Single> { - return local.getGrades(semester).map { it.filter { grade -> !grade.isRead } }.toSingle(emptyList()) - } - - fun getNotNotifiedGrades(semester: Semester): Single> { - return local.getGrades(semester).map { it.filter { grade -> !grade.isNotified } }.toSingle(emptyList()) - } - - fun updateGrade(grade: Grade): Completable { - return Completable.fromCallable { local.updateGrades(listOf(grade)) } - } - - fun updateGrades(grades: List): Completable { - return Completable.fromCallable { local.updateGrades(grades) } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryLocal.kt deleted file mode 100644 index e74641d3a..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryLocal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradessummary - -import io.github.wulkanowy.data.db.dao.GradeSummaryDao -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeSummaryLocal @Inject constructor(private val gradeSummaryDb: GradeSummaryDao) { - - fun saveGradesSummary(gradesSummary: List) { - gradeSummaryDb.insertAll(gradesSummary) - } - - fun deleteGradesSummary(gradesSummary: List) { - gradeSummaryDb.deleteAll(gradesSummary) - } - - fun getGradesSummary(semester: Semester): Maybe> { - return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt deleted file mode 100644 index d395decf8..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradessummary - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeSummaryRemote @Inject constructor(private val api: Api) { - - fun getGradeSummary(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getGradesSummary(semester.semesterId) } - .map { gradesSummary -> - gradesSummary.map { - GradeSummary( - semesterId = semester.semesterId, - studentId = semester.studentId, - subject = it.name, - predictedGrade = it.predicted, - finalGrade = it.final - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt deleted file mode 100644 index b19e07f00..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradessummary - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Single -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeSummaryRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: GradeSummaryLocal, - private val remote: GradeSummaryRemote -) { - - fun getGradesSummary(semester: Semester, forceRefresh: Boolean = false): Single> { - return local.getGradesSummary(semester).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getGradeSummary(semester) - else Single.error(UnknownHostException()) - }.flatMap { newGradesSummary -> - local.getGradesSummary(semester).toSingle(emptyList()) - .doOnSuccess { oldGradesSummary -> - local.deleteGradesSummary(oldGradesSummary - newGradesSummary) - local.saveGradesSummary(newGradesSummary - oldGradesSummary) - } - }.flatMap { local.getGradesSummary(semester).toSingle(emptyList()) }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt deleted file mode 100644 index 581ac2f81..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradestatistics - -import io.github.wulkanowy.data.db.dao.GradeStatisticsDao -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeStatisticsLocal @Inject constructor(private val gradeStatisticsDb: GradeStatisticsDao) { - - fun getGradesStatistics(semester: Semester, isSemester: Boolean): Maybe> { - return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester) - .filter { !it.isEmpty() } - } - - fun getGradesStatistics(semester: Semester, isSemester: Boolean, subjectName: String): Maybe> { - return (if ("Wszystkie" == subjectName) gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester).map { list -> - list.groupBy { it.grade }.map { - GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key, it.value.fold(0) { acc, e -> acc + e.amount }, false) - } - } - else gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)).filter { !it.isEmpty() } - } - - fun saveGradesStatistics(gradesStatistics: List) { - gradeStatisticsDb.insertAll(gradesStatistics) - } - - fun deleteGradesStatistics(gradesStatistics: List) { - gradeStatisticsDb.deleteAll(gradesStatistics) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt deleted file mode 100644 index fa3b951f6..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradestatistics - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeStatisticsRemote @Inject constructor(private val api: Api) { - - fun getGradeStatistics(semester: Semester, isSemester: Boolean): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getGradesStatistics(semester.semesterId, isSemester) } - .map { gradeStatistics -> - gradeStatistics.map { - GradeStatistics( - semesterId = semester.semesterId, - studentId = semester.studentId, - subject = it.subject, - grade = it.gradeValue, - amount = it.amount ?: 0, - semester = isSemester - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt deleted file mode 100644 index 870bd1e93..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradestatistics - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Single -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeStatisticsRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: GradeStatisticsLocal, - private val remote: GradeStatisticsRemote -) { - - fun getGradesStatistics(semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single> { - return local.getGradesStatistics(semester, isSemester, subjectName).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getGradeStatistics(semester, isSemester) - else Single.error(UnknownHostException()) - }.flatMap { newGradesStats -> - local.getGradesStatistics(semester, isSemester).toSingle(emptyList()) - .doOnSuccess { oldGradesStats -> - local.deleteGradesStatistics(oldGradesStats - newGradesStats) - local.saveGradesStatistics(newGradesStats - oldGradesStats) - } - }.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).toSingle(emptyList()) }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkLocal.kt deleted file mode 100644 index 671ecafd7..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkLocal.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.repositories.homework - -import io.github.wulkanowy.data.db.dao.HomeworkDao -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) { - - fun saveHomework(homework: List) { - homeworkDb.insertAll(homework) - } - - fun deleteHomework(homework: List) { - homeworkDb.deleteAll(homework) - } - - fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { - return homeworkDb.loadAll(semester.semesterId, semester.studentId, startDate, endDate) - .filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt deleted file mode 100644 index 681b66469..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.repositories.homework - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate -import io.reactivex.Single -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class HomeworkRemote @Inject constructor(private val api: Api) { - - fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getHomework(startDate, endDate) } - .map { homework -> - homework.map { - Homework( - semesterId = semester.semesterId, - studentId = semester.studentId, - date = it.date.toLocalDate(), - entryDate = it.entryDate.toLocalDate(), - subject = it.subject, - content = it.content, - teacher = it.teacher, - teacherSymbol = it.teacherSymbol - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt deleted file mode 100644 index 0447c86fe..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.data.repositories.homework - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Single -import org.threeten.bp.LocalDate -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class HomeworkRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: HomeworkLocal, - private val remote: HomeworkRemote -) { - - fun getHomework(semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single> { - return Single.fromCallable { start.monday to end.friday }.flatMap { (monday, friday) -> - local.getHomework(semester, monday, friday).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getHomework(semester, monday, friday) - else Single.error(UnknownHostException()) - }.flatMap { newGrades -> - local.getHomework(semester, monday, friday).toSingle(emptyList()) - .doOnSuccess { oldGrades -> - local.deleteHomework(oldGrades - newGrades) - local.saveHomework(newGrades - oldGrades) - } - }.flatMap { local.getHomework(semester, monday, friday).toSingle(emptyList()) }) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt deleted file mode 100644 index 115c89652..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.repositories.luckynumber - -import io.github.wulkanowy.data.db.dao.LuckyNumberDao -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumberDao) { - - fun saveLuckyNumber(luckyNumber: LuckyNumber) { - luckyNumberDb.insert(luckyNumber) - } - - fun updateLuckyNumber(luckyNumber: LuckyNumber) { - luckyNumberDb.update(luckyNumber) - } - - fun deleteLuckyNumber(luckyNumber: LuckyNumber) { - luckyNumberDb.delete(luckyNumber) - } - - fun getLuckyNumber(semester: Semester, date: LocalDate): Maybe { - return luckyNumberDb.load(semester.studentId, date) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt deleted file mode 100644 index 1b0f12b3e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.repositories.luckynumber - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import io.reactivex.Single -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class LuckyNumberRemote @Inject constructor(private val api: Api) { - - fun getLuckyNumber(semester: Semester): Maybe { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMapMaybe { it.getLuckyNumber() } - .map { - LuckyNumber( - studentId = semester.studentId, - date = LocalDate.now(), - luckyNumber = it - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt deleted file mode 100644 index 4036521f7..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.wulkanowy.data.repositories.luckynumber - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Completable -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class LuckyNumberRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: LuckyNumberLocal, - private val remote: LuckyNumberRemote -) { - - fun getLuckyNumber(semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Maybe { - return local.getLuckyNumber(semester, LocalDate.now()).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMapMaybe { - if (it) remote.getLuckyNumber(semester) - else Maybe.error(UnknownHostException()) - }.flatMap { new -> - local.getLuckyNumber(semester, LocalDate.now()) - .doOnSuccess { old -> - if (new != old) { - local.deleteLuckyNumber(old) - local.saveLuckyNumber(new.apply { - if (notify) isNotified = false - }) - } - } - .doOnComplete { - local.saveLuckyNumber(new.apply { - if (notify) isNotified = false - }) - } - }.flatMap({ local.getLuckyNumber(semester, LocalDate.now()) }, { Maybe.error(it) }, - { local.getLuckyNumber(semester, LocalDate.now()) }) - ) - } - - fun getNotNotifiedLuckyNumber(semester: Semester): Maybe { - return local.getLuckyNumber(semester, LocalDate.now()).filter { !it.isNotified } - } - - fun updateLuckyNumber(luckyNumber: LuckyNumber): Completable { - return Completable.fromCallable { local.updateLuckyNumber(luckyNumber) } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt deleted file mode 100644 index 06f5a1e05..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.wulkanowy.data.repositories.message - -enum class MessageFolder(val id: Int = 1) { - RECEIVED(1), - SENT(2), - TRASHED(3) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt deleted file mode 100644 index e9ab72973..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.data.repositories.message - -import io.github.wulkanowy.data.db.dao.MessagesDao -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) { - - fun saveMessages(messages: List) { - messagesDb.insertAll(messages) - } - - fun updateMessages(messages: List) { - messagesDb.updateAll(messages) - } - - fun deleteMessages(messages: List) { - messagesDb.deleteAll(messages) - } - - fun getMessage(student: Student, id: Int): Maybe { - return messagesDb.load(student.studentId, id) - } - - fun getMessages(student: Student, folder: MessageFolder): Maybe> { - return when (folder) { - TRASHED -> messagesDb.loadDeleted(student.studentId) - else -> messagesDb.loadAll(student.studentId, folder.id) - }.filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt deleted file mode 100644 index 3fb01d30e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt +++ /dev/null @@ -1,61 +0,0 @@ -package io.github.wulkanowy.data.repositories.message - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.api.messages.Folder -import io.github.wulkanowy.api.messages.SentMessage -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.utils.toLocalDateTime -import io.reactivex.Single -import org.threeten.bp.LocalDateTime.now -import javax.inject.Inject -import javax.inject.Singleton -import io.github.wulkanowy.api.messages.Message as ApiMessage -import io.github.wulkanowy.api.messages.Recipient as ApiRecipient - -@Singleton -class MessageRemote @Inject constructor(private val api: Api) { - - fun getMessages(studentId: Int, folder: MessageFolder): Single> { - return api.getMessages(Folder.valueOf(folder.name)).map { messages -> - messages.map { - Message( - studentId = studentId, - realId = it.id ?: 0, - messageId = it.messageId ?: 0, - sender = it.sender.orEmpty(), - senderId = it.senderId ?: 0, - recipient = it.recipient.orEmpty(), - subject = it.subject.trim(), - date = it.date?.toLocalDateTime() ?: now(), - folderId = it.folderId, - unread = it.unread ?: false, - unreadBy = it.unreadBy ?: 0, - readBy = it.readBy ?: 0, - removed = it.removed - ) - } - } - } - - fun getMessagesContent(message: Message, markAsRead: Boolean = false): Single { - return api.getMessageContent(message.messageId, message.folderId, markAsRead, message.realId) - } - - fun sendMessage(subject: String, content: String, recipients: List): Single { - return api.sendMessage( - subject = subject, - content = content, - recipients = recipients.map { - ApiRecipient( - id = it.realId, - name = it.realName, - loginId = it.loginId, - reportingUnitId = it.unitId, - role = it.role, - hash = it.hash - ) - } - ) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt deleted file mode 100644 index d319689e4..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt +++ /dev/null @@ -1,92 +0,0 @@ -package io.github.wulkanowy.data.repositories.message - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.api.messages.SentMessage -import io.github.wulkanowy.data.ApiHelper -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED -import io.reactivex.Completable -import io.reactivex.Single -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class MessageRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: MessageLocal, - private val remote: MessageRemote, - private val apiHelper: ApiHelper -) { - - fun getMessages(student: Student, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): Single> { - return Single.just(apiHelper.initApi(student)) - .flatMap { _ -> - local.getMessages(student, folder).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getMessages(student.studentId, folder) - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getMessages(student, folder).toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteMessages(old - new) - local.saveMessages((new - old) - .onEach { - it.isNotified = !notify - }) - } - }.flatMap { local.getMessages(student, folder).toSingle(emptyList()) } - ) - } - } - - fun getMessage(student: Student, messageId: Int, markAsRead: Boolean = false): Single { - return Single.just(apiHelper.initApi(student)) - .flatMap { _ -> - local.getMessage(student, messageId) - .filter { !it.content.isNullOrEmpty() } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) local.getMessage(student, messageId).toSingle() - else Single.error(UnknownHostException()) - } - .flatMap { dbMessage -> - remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess { - local.updateMessages(listOf(dbMessage.copy(unread = false).apply { - id = dbMessage.id - content = it - })) - } - }.flatMap { - local.getMessage(student, messageId).toSingle() - } - ) - } - } - - fun getNotNotifiedMessages(student: Student): Single> { - return local.getMessages(student, RECEIVED) - .map { it.filter { message -> !message.isNotified && message.unread } } - .toSingle(emptyList()) - } - - fun updateMessage(message: Message): Completable { - return Completable.fromCallable { local.updateMessages(listOf(message)) } - } - - fun updateMessages(messages: List): Completable { - return Completable.fromCallable { local.updateMessages(messages) } - } - - fun sendMessage(subject: String, content: String, recipients: List): Single { - return ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.sendMessage(subject, content, recipients) - else Single.error(UnknownHostException()) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt deleted file mode 100644 index 784e61f0f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.repositories.note - -import io.github.wulkanowy.data.db.dao.NoteDao -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class NoteLocal @Inject constructor(private val noteDb: NoteDao) { - - fun saveNotes(notes: List) { - noteDb.insertAll(notes) - } - - fun updateNotes(notes: List) { - noteDb.updateAll(notes) - } - - fun deleteNotes(notes: List) { - noteDb.deleteAll(notes) - } - - fun getNotes(student: Student): Maybe> { - return noteDb.loadAll(student.studentId).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt deleted file mode 100644 index aebc6230e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.repositories.note - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class NoteRemote @Inject constructor(private val api: Api) { - - fun getNotes(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getNotes() } - .map { notes -> - notes.map { - Note( - studentId = semester.studentId, - date = it.date.toLocalDate(), - teacher = it.teacher, - category = it.category, - content = it.content - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt deleted file mode 100644 index d74bc7eaf..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.data.repositories.note - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Completable -import io.reactivex.Single -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class NoteRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: NoteLocal, - private val remote: NoteRemote -) { - - fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Single> { - return local.getNotes(student).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getNotes(semester) - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getNotes(student).toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteNotes(old - new) - local.saveNotes((new - old) - .onEach { - if (it.date >= student.registrationDate.toLocalDate()) it.apply { - isRead = false - if (notify) isNotified = false - } - }) - } - }.flatMap { local.getNotes(student).toSingle(emptyList()) }) - } - - fun getNotNotifiedNotes(student: Student): Single> { - return local.getNotes(student).map { it.filter { note -> !note.isNotified } }.toSingle(emptyList()) - } - - fun updateNote(note: Note): Completable { - return Completable.fromCallable { local.updateNotes(listOf(note)) } - } - - fun updateNotes(notes: List): Completable { - return Completable.fromCallable { local.updateNotes(notes) } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt deleted file mode 100644 index d51fc4957..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.wulkanowy.data.repositories.preferences - -import android.content.Context -import android.content.SharedPreferences -import io.github.wulkanowy.R -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class PreferencesRepository @Inject constructor( - private val sharedPref: SharedPreferences, - val context: Context -) { - val startMenuIndex: Int - get() = sharedPref.getString(context.getString(R.string.pref_key_start_menu), "0")?.toIntOrNull() ?: 0 - - val isShowPresent: Boolean - get() = sharedPref.getBoolean(context.getString(R.string.pref_key_attendance_present), true) - - val isGradeExpandable: Boolean - get() = !sharedPref.getBoolean(context.getString(R.string.pref_key_expand_grade), false) - - val currentThemeKey: String = context.getString(R.string.pref_key_theme) - val currentTheme: Int - get() = sharedPref.getString(currentThemeKey, "1")?.toIntOrNull() ?: 1 - - val gradeColorTheme: String - get() = sharedPref.getString(context.getString(R.string.pref_key_grade_color_scheme), "vulcan") ?: "vulcan" - - val serviceEnableKey: String = context.getString(R.string.pref_key_services_enable) - val isServiceEnabled: Boolean - get() = sharedPref.getBoolean(serviceEnableKey, true) - - val servicesIntervalKey: String = context.getString(R.string.pref_key_services_interval) - val servicesInterval: Long - get() = sharedPref.getString(servicesIntervalKey, "60")?.toLongOrNull() ?: 60 - - val servicesOnlyWifiKey: String = context.getString(R.string.pref_key_services_wifi_only) - val isServicesOnlyWifi: Boolean - get() = sharedPref.getBoolean(servicesOnlyWifiKey, true) - - val isNotificationsEnable: Boolean - get() = sharedPref.getBoolean(context.getString(R.string.pref_key_notifications_enable), true) - - val isDebugNotificationEnableKey: String = context.getString(R.string.pref_key_notification_debug) - val isDebugNotificationEnable: Boolean - get() = sharedPref.getBoolean(isDebugNotificationEnableKey, false) - - val gradePlusModifier: Double - get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0 - - val gradeMinusModifier: Double - get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble() - ?: 0.0 - - val fillMessageContent: Boolean - get() = sharedPref.getBoolean(context.getString(R.string.pref_key_fill_message_content), true) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt deleted file mode 100644 index 6b8328ec2..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.recipient - -import io.github.wulkanowy.data.db.dao.RecipientDao -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class RecipientLocal @Inject constructor(private val recipientDb: RecipientDao) { - - fun getRecipients(student: Student, role: Int, unit: ReportingUnit): Maybe> { - return recipientDb.load(student.studentId, role, unit.realId).filter { !it.isEmpty() } - } - - fun saveRecipients(recipients: List) { - return recipientDb.insertAll(recipients) - } - - fun deleteRecipients(recipients: List) { - recipientDb.deleteAll(recipients) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt deleted file mode 100644 index b726edda9..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.data.repositories.recipient - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton -import io.github.wulkanowy.api.messages.Recipient as ApiRecipient - -@Singleton -class RecipientRemote @Inject constructor(private val api: Api) { - - fun getRecipients(role: Int, unit: ReportingUnit): Single> { - return api.getRecipients(unit.realId, role) - .map { recipients -> - recipients.map { it.toRecipient() } - } - } - - fun getMessageRecipients(message: Message): Single> { - return api.getMessageRecipients(message.messageId, message.senderId) - .map { recipients -> - recipients.map { it.toRecipient() } - } - } - - private fun ApiRecipient.toRecipient(): Recipient { - return Recipient( - studentId = api.studentId, - realId = id, - realName = name, - name = shortName, - hash = hash, - loginId = loginId, - role = role, - unitId = reportingUnitId ?: 0 - ) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt deleted file mode 100644 index 0b02721f1..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.data.repositories.recipient - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.ApiHelper -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Single -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class RecipientRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: RecipientLocal, - private val remote: RecipientRemote, - private val apiHelper: ApiHelper -) { - - fun getRecipients(student: Student, role: Int, unit: ReportingUnit, forceRefresh: Boolean = false): Single> { - return Single.just(apiHelper.initApi(student)) - .flatMap { _ -> - local.getRecipients(student, role, unit).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getRecipients(role, unit) - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getRecipients(student, role, unit).toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteRecipients(old - new) - local.saveRecipients(new - old) - } - }.flatMap { - local.getRecipients(student, role, unit).toSingle(emptyList()) - } - ) - } - } - - fun getMessageRecipients(student: Student, message: Message): Single> { - return Single.just(apiHelper.initApi(student)) - .flatMap { ReactiveNetwork.checkInternetConnectivity(settings) } - .flatMap { - if (it) remote.getMessageRecipients(message) - else Single.error(UnknownHostException()) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt deleted file mode 100644 index b4281cbfd..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.repositories.reportingunit - -import io.github.wulkanowy.data.db.dao.ReportingUnitDao -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ReportingUnitLocal @Inject constructor(private val reportingUnitDb: ReportingUnitDao) { - - fun getReportingUnits(student: Student): Maybe> { - return reportingUnitDb.load(student.studentId).filter { !it.isEmpty() } - } - - fun getReportingUnit(student: Student, unitId: Int): Maybe { - return reportingUnitDb.loadOne(student.studentId, unitId) - } - - fun saveReportingUnits(reportingUnits: List) { - return reportingUnitDb.insertAll(reportingUnits) - } - - fun deleteReportingUnits(reportingUnits: List) { - reportingUnitDb.deleteAll(reportingUnits) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt deleted file mode 100644 index feb4b0134..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.repositories.reportingunit - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ReportingUnitRemote @Inject constructor(private val api: Api) { - - fun getReportingUnits(): Single> { - return api.getReportingUnits().map { - it.map { unit -> - ReportingUnit( - studentId = api.studentId, - realId = unit.id, - roles = unit.roles, - senderId = unit.senderId, - senderName = unit.senderName, - shortName = unit.short - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt deleted file mode 100644 index 9184b4bb1..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.data.repositories.reportingunit - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.ApiHelper -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Maybe -import io.reactivex.Single -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ReportingUnitRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: ReportingUnitLocal, - private val remote: ReportingUnitRemote, - private val apiHelper: ApiHelper -) { - - fun getReportingUnits(student: Student, forceRefresh: Boolean = false): Single> { - return Single.just(apiHelper.initApi(student)) - .flatMap { _ -> - local.getReportingUnits(student).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getReportingUnits() - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getReportingUnits(student).toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteReportingUnits(old - new) - local.saveReportingUnits(new - old) - } - }.flatMap { local.getReportingUnits(student).toSingle(emptyList()) } - ) - } - } - - fun getReportingUnit(student: Student, unitId: Int): Maybe { - return Maybe.just(apiHelper.initApi(student)) - .flatMap { _ -> - local.getReportingUnit(student, unitId) - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) getReportingUnits(student, true) - else Single.error(UnknownHostException()) - }.flatMapMaybe { - local.getReportingUnit(student, unitId) - } - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt deleted file mode 100644 index b9750e7d5..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.repositories.semester - -import io.github.wulkanowy.data.db.dao.SemesterDao -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) { - - fun saveSemesters(semesters: List) { - semesterDb.insertAll(semesters) - } - - fun deleteSemesters(semesters: List) { - semesterDb.deleteAll(semesters) - } - - fun getSemesters(student: Student): Maybe> { - return semesterDb.loadAll(student.studentId, student.classId).filter { !it.isEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt deleted file mode 100644 index b25c8881b..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.data.repositories.semester - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SemesterRemote @Inject constructor(private val api: Api) { - - fun getSemesters(student: Student): Single> { - return api.getSemesters().map { semesters -> - semesters.map { semester -> - Semester( - studentId = student.studentId, - diaryId = semester.diaryId, - diaryName = semester.diaryName, - semesterId = semester.semesterId, - semesterName = semester.semesterNumber, - isCurrent = semester.current, - classId = semester.classId, - unitId = semester.unitId - ) - } - - } - } -} - - diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt deleted file mode 100644 index 9735f0290..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.wulkanowy.data.repositories.semester - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.ApiHelper -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Maybe -import io.reactivex.Single -import timber.log.Timber -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SemesterRepository @Inject constructor( - private val remote: SemesterRemote, - private val local: SemesterLocal, - private val settings: InternetObservingSettings, - private val apiHelper: ApiHelper -) { - - fun getSemesters(student: Student, forceRefresh: Boolean = false): Single> { - return Maybe.just(apiHelper.initApi(student)) - .flatMap { local.getSemesters(student).filter { !forceRefresh } } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getSemesters(student) else Single.error(UnknownHostException()) - }.flatMap { new -> - val currentSemesters = new.filter { it.isCurrent } - if (currentSemesters.size == 1) { - local.getSemesters(student).toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteSemesters(old - new) - local.saveSemesters(new - old) - } - } else { - Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}") - throw IllegalArgumentException("Current semester can be only one.") - } - }.flatMap { local.getSemesters(student).toSingle(emptyList()) }) - } - - fun getCurrentSemester(student: Student, forceRefresh: Boolean = false): Single { - return getSemesters(student, forceRefresh).map { item -> item.single { it.isCurrent } } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt deleted file mode 100644 index 7bbd283fb..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.wulkanowy.data.repositories.student - -import android.content.Context -import io.github.wulkanowy.data.db.dao.StudentDao -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.security.decrypt -import io.github.wulkanowy.utils.security.encrypt -import io.reactivex.Completable -import io.reactivex.Maybe -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class StudentLocal @Inject constructor( - private val studentDb: StudentDao, - private val context: Context -) { - - fun saveStudent(student: Student): Single { - return Single.fromCallable { studentDb.insert(student.copy(password = encrypt(student.password, context))) } - } - - fun getStudents(decryptPass: Boolean): Maybe> { - return studentDb.loadAll() - .map { list -> list.map { it.apply { if (decryptPass) password = decrypt(password) } } } - .filter { !it.isEmpty() } - } - - fun getCurrentStudent(decryptPass: Boolean): Maybe { - return studentDb.loadCurrent().map { it.apply { if (decryptPass) password = decrypt(password) } } - } - - fun setCurrentStudent(student: Student): Completable { - return Completable.fromCallable { - studentDb.run { - resetCurrent() - updateCurrent(student.id) - } - } - } - - fun logoutStudent(student: Student): Completable { - return Completable.fromCallable { studentDb.delete(student) } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt deleted file mode 100644 index f0692a534..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.repositories.student - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Single -import org.threeten.bp.LocalDateTime.now -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class StudentRemote @Inject constructor(private val api: Api) { - - fun getStudents(email: String, password: String, endpoint: String): Single> { - return api.getStudents().map { students -> - students.map { student -> - Student( - email = email, - password = password, - symbol = student.symbol, - studentId = student.studentId, - studentName = student.studentName, - schoolSymbol = student.schoolSymbol, - schoolName = student.schoolName, - classId = student.classId, - endpoint = endpoint, - loginType = student.loginType.name, - isCurrent = false, - registrationDate = now() - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt deleted file mode 100644 index b4b7c8289..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.data.repositories.student - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.ApiHelper -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.exceptions.NoCurrentStudentException -import io.reactivex.Completable -import io.reactivex.Maybe -import io.reactivex.Single -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class StudentRepository @Inject constructor( - private val local: StudentLocal, - private val remote: StudentRemote, - private val settings: InternetObservingSettings, - private val apiHelper: ApiHelper -) { - - fun isStudentSaved(): Single = local.getStudents(false).isEmpty.map { !it } - - fun getStudents(email: String, password: String, endpoint: String, symbol: String = ""): Single> { - return ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - apiHelper.initApi(email, password, symbol, endpoint) - if (it) remote.getStudents(email, password, endpoint) - else Single.error(UnknownHostException("No internet connection")) - } - } - - fun getSavedStudents(decryptPass: Boolean = true): Single> { - return local.getStudents(decryptPass).toSingle(emptyList()) - } - - fun getCurrentStudent(decryptPass: Boolean = true): Single { - return local.getCurrentStudent(decryptPass) - .switchIfEmpty(Maybe.error(NoCurrentStudentException())) - .toSingle() - } - - fun saveStudent(student: Student): Single { - return local.saveStudent(student) - } - - fun switchStudent(student: Student): Completable { - return local.setCurrentStudent(student) - } - - fun logoutStudent(student: Student): Completable { - return local.logoutStudent(student) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectLocal.kt deleted file mode 100644 index 63e334019..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.subject - -import io.github.wulkanowy.data.db.dao.SubjectDao -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Subject -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SubjectLocal @Inject constructor(private val subjectDao: SubjectDao) { - - fun getSubjects(semester: Semester): Maybe> { - return subjectDao.loadAll(semester.diaryId, semester.studentId) - .filter { !it.isEmpty() } - } - - fun saveSubjects(subjects: List) { - subjectDao.insertAll(subjects) - } - - fun deleteSubjects(subjects: List) { - subjectDao.deleteAll(subjects) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt deleted file mode 100644 index 88fbb196b..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.data.repositories.subject - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Subject -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SubjectRemote @Inject constructor(private val api: Api) { - - fun getSubjects(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { api.getSubjects() } - .map { subjects -> - subjects.map { - Subject( - studentId = semester.studentId, - diaryId = semester.diaryId, - name = it.name, - realId = it.value - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt deleted file mode 100644 index 6167251b9..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.data.repositories.subject - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Subject -import io.reactivex.Single -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SubjectRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: SubjectLocal, - private val remote: SubjectRemote -) { - - fun getSubjects(semester: Semester, forceRefresh: Boolean = false): Single> { - return local.getSubjects(semester).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getSubjects(semester) - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getSubjects(semester) - .toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteSubjects(old - new) - local.saveSubjects(new - old) - } - }.flatMap { - local.getSubjects(semester).toSingle(emptyList()) - }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocal.kt deleted file mode 100644 index e074ce2a1..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import io.github.wulkanowy.data.db.dao.TimetableDao -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Timetable -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao) { - - fun saveTimetable(timetables: List) { - timetableDb.insertAll(timetables) - } - - fun deleteTimetable(timetables: List) { - timetableDb.deleteAll(timetables) - } - - fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { - return timetableDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt deleted file mode 100644 index 77742e7b3..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.utils.toLocalDate -import io.github.wulkanowy.utils.toLocalDateTime -import io.reactivex.Single -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class TimetableRemote @Inject constructor(private val api: Api) { - - fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getTimetable(startDate, endDate) } - .map { lessons -> - lessons.map { - Timetable( - studentId = semester.studentId, - diaryId = semester.diaryId, - number = it.number, - start = it.start.toLocalDateTime(), - end = it.end.toLocalDateTime(), - date = it.date.toLocalDate(), - subject = it.subject, - subjectOld = it.subjectOld, - group = it.group, - room = it.room, - roomOld = it.roomOld, - teacher = it.teacher, - teacherOld = it.teacherOld, - info = it.info, - changes = it.changes, - canceled = it.canceled - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt deleted file mode 100644 index 460d55fd7..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt +++ /dev/null @@ -1,49 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Single -import org.threeten.bp.LocalDate -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class TimetableRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: TimetableLocal, - private val remote: TimetableRemote -) { - - fun getTimetable(semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single> { - return Single.fromCallable { start.monday to end.friday }.flatMap { (monday, friday) -> - local.getTimetable(semester, monday, friday).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap { - if (it) remote.getTimetable(semester, monday, friday) - else Single.error(UnknownHostException()) - }.flatMap { newTimetable -> - local.getTimetable(semester, monday, friday) - .toSingle(emptyList()) - .doOnSuccess { oldTimetable -> - local.deleteTimetable(oldTimetable - newTimetable) - local.saveTimetable((newTimetable - oldTimetable).map { item -> - item.apply { - oldTimetable.singleOrNull { this.start == it.start }?.let { - return@map copy( - room = if (room.isEmpty()) it.room else room, - teacher = if (teacher.isEmpty()) it.teacher else teacher - ) - } - } - }) - } - }.flatMap { - local.getTimetable(semester, monday, friday).toSingle(emptyList()) - }).map { list -> list.filter { it.date in start..end } } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/SyncContract.java b/app/src/main/java/io/github/wulkanowy/data/sync/SyncContract.java new file mode 100644 index 000000000..d9850ab12 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/SyncContract.java @@ -0,0 +1,11 @@ +package io.github.wulkanowy.data.sync; + +import java.io.IOException; +import java.text.ParseException; + +import io.github.wulkanowy.api.VulcanException; + +public interface SyncContract { + + void sync() throws VulcanException, IOException, ParseException; +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSync.java new file mode 100644 index 000000000..d56f99905 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSync.java @@ -0,0 +1,78 @@ +package io.github.wulkanowy.data.sync.account; + +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.VulcanException; +import io.github.wulkanowy.data.db.dao.entities.Account; +import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.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 AccountSync implements AccountSyncContract { + + private final DaoSession daoSession; + + private final SharedPrefContract sharedPref; + + private final Vulcan vulcan; + + private final Context context; + + @Inject + AccountSync(DaoSession daoSession, SharedPrefContract sharedPref, + Vulcan vulcan, @ApplicationContext Context context) { + this.daoSession = daoSession; + this.sharedPref = sharedPref; + this.vulcan = vulcan; + this.context = context; + } + + @Override + public void registerUser(String email, String password, String symbol) + throws VulcanException, IOException, CryptoException { + + LogUtils.debug("Register new user email=" + email); + + vulcan.setCredentials(email, password, symbol, null); + + Account account = new Account() + .setName(vulcan.getBasicInformation().getPersonalData().getFirstAndLastName()) + .setEmail(email) + .setPassword(Scrambler.encrypt(email, password, context)) + .setSymbol(vulcan.getSymbol()) + .setSnpId(vulcan.getStudentAndParent().getId()); + + daoSession.getAccountDao().insert(account); + + sharedPref.setCurrentUserId(account.getId()); + } + + @Override + public void initLastUser() throws VulcanException, IOException, CryptoException { + + long userId = sharedPref.getCurrentUserId(); + + if (userId == 0) { + throw new IOException("Can't find saved user"); + } + + LogUtils.debug("Initialization current user id=" + userId); + + Account account = daoSession.getAccountDao().load(userId); + + vulcan.setCredentials(account.getEmail(), + Scrambler.decrypt(account.getEmail(), account.getPassword()), + account.getSymbol(), + account.getSnpId()); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSyncContract.java b/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSyncContract.java new file mode 100644 index 000000000..799d320e3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/account/AccountSyncContract.java @@ -0,0 +1,16 @@ +package io.github.wulkanowy.data.sync.account; + +import java.io.IOException; + +import io.github.wulkanowy.api.VulcanException; +import io.github.wulkanowy.utils.security.CryptoException; + +public interface AccountSyncContract { + + void registerUser(String email, String password, String symbol) + throws VulcanException, IOException, + CryptoException; + + void initLastUser() throws VulcanException, IOException, + CryptoException; +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSync.java new file mode 100644 index 000000000..325fff196 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSync.java @@ -0,0 +1,156 @@ +package io.github.wulkanowy.data.sync.attendance; + +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.github.wulkanowy.api.Vulcan; +import io.github.wulkanowy.api.VulcanException; +import io.github.wulkanowy.api.generic.Lesson; +import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; +import io.github.wulkanowy.data.db.dao.entities.AttendanceLessonDao; +import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.dao.entities.Day; +import io.github.wulkanowy.data.db.dao.entities.DayDao; +import io.github.wulkanowy.data.db.dao.entities.Week; +import io.github.wulkanowy.data.db.dao.entities.WeekDao; +import io.github.wulkanowy.data.db.shared.SharedPrefContract; +import io.github.wulkanowy.utils.DataObjectConverter; +import io.github.wulkanowy.utils.LogUtils; +import io.github.wulkanowy.utils.TimeUtils; + +@Singleton +public class AttendanceSync implements AttendanceSyncContract { + + private final DaoSession daoSession; + + private final SharedPrefContract sharedPref; + + private final Vulcan vulcan; + + private long userId; + + @Inject + AttendanceSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) { + this.daoSession = daoSession; + this.sharedPref = sharedPref; + this.vulcan = vulcan; + } + + @Override + public void syncAttendance() throws IOException, ParseException, VulcanException { + syncAttendance(null); + } + + @Override + public void syncAttendance(String date) throws IOException, ParseException, VulcanException { + this.userId = sharedPref.getCurrentUserId(); + + io.github.wulkanowy.api.generic.Week weekApi = getWeekFromApi(getNormalizedDate(date)); + Week weekDb = getWeekFromDb(weekApi.getStartDayDate()); + + long weekId = updateWeekInDb(weekDb, weekApi); + + List lessonList = updateDays(weekApi.getDays(), weekId); + + daoSession.getAttendanceLessonDao().saveInTx(lessonList); + + LogUtils.debug("Synchronization attendance lessons (amount = " + lessonList.size() + ")"); + } + + private String getNormalizedDate(String date) throws ParseException { + return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : ""; + } + + private io.github.wulkanowy.api.generic.Week getWeekFromApi(String date) + throws IOException, ParseException, VulcanException { + return vulcan.getAttendanceTable().getWeekTable(date); + } + + private Week getWeekFromDb(String date) { + return daoSession.getWeekDao() + .queryBuilder() + .where(WeekDao.Properties.UserId.eq(userId), WeekDao.Properties.StartDayDate.eq(date)) + .unique(); + } + + private Long updateWeekInDb(Week dbWeekEntity, io.github.wulkanowy.api.generic.Week fromApi) { + if (dbWeekEntity != null) { + dbWeekEntity.setIsAttendanceSynced(true); + dbWeekEntity.update(); + + return dbWeekEntity.getId(); + } + + Week apiWeekEntity = DataObjectConverter.weekToWeekEntity(fromApi).setUserId(userId); + apiWeekEntity.setIsAttendanceSynced(true); + + return daoSession.getWeekDao().insert(apiWeekEntity); + } + + private List updateDays(List dayListFromApi, long weekId) { + List updatedLessonList = new ArrayList<>(); + + for (io.github.wulkanowy.api.generic.Day dayFromApi : dayListFromApi) { + + Day dbDayEntity = getDayFromDb(dayFromApi.getDate()); + + Day apiDayEntity = DataObjectConverter.dayToDayEntity(dayFromApi); + + long dayId = updateDay(dbDayEntity, apiDayEntity, weekId); + + updateLessons(dayFromApi.getLessons(), updatedLessonList, dayId); + } + + return updatedLessonList; + } + + private Day getDayFromDb(String date) { + return daoSession.getDayDao() + .queryBuilder() + .where(DayDao.Properties.UserId.eq(userId), DayDao.Properties.Date.eq(date)) + .unique(); + } + + private long updateDay(Day dbDayEntity, Day apiDayEntity, long weekId) { + if (null != dbDayEntity) { + return dbDayEntity.getId(); + } + + apiDayEntity.setUserId(userId); + apiDayEntity.setWeekId(weekId); + + return daoSession.getDayDao().insert(apiDayEntity); + } + + private void updateLessons(List lessons, List updatedLessons, long dayId) { + List lessonsFromApiEntities = DataObjectConverter + .lessonsToAttendanceLessonsEntities(lessons); + + for (AttendanceLesson apiLessonEntity : lessonsFromApiEntities) { + AttendanceLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId); + + apiLessonEntity.setDayId(dayId); + + if (lessonFromDb != null) { + apiLessonEntity.setId(lessonFromDb.getId()); + } + + if (!"".equals(apiLessonEntity.getSubject())) { + updatedLessons.add(apiLessonEntity); + } + } + } + + private AttendanceLesson getLessonFromDb(AttendanceLesson apiEntity, long dayId) { + return daoSession.getAttendanceLessonDao().queryBuilder() + .where(AttendanceLessonDao.Properties.DayId.eq(dayId), + AttendanceLessonDao.Properties.Date.eq(apiEntity.getDate()), + AttendanceLessonDao.Properties.Number.eq(apiEntity.getNumber())) + .unique(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSyncContract.java b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSyncContract.java new file mode 100644 index 000000000..dfb4c1b4a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/attendance/AttendanceSyncContract.java @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.sync.attendance; + +import java.io.IOException; +import java.text.ParseException; + +import io.github.wulkanowy.api.VulcanException; + +public interface AttendanceSyncContract { + + void syncAttendance(String date) throws IOException, ParseException, VulcanException; + + void syncAttendance() throws IOException, ParseException, VulcanException; +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/grades/GradeSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/grades/GradeSync.java new file mode 100644 index 000000000..b132ac1b1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/grades/GradeSync.java @@ -0,0 +1,91 @@ +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.VulcanException; +import io.github.wulkanowy.data.db.dao.entities.Account; +import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.dao.entities.Grade; +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; + + private Long userId; + + @Inject + GradeSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) { + this.daoSession = daoSession; + this.sharedPref = sharedPref; + this.vulcan = vulcan; + } + + @Override + public void sync() throws IOException, VulcanException, ParseException { + + userId = sharedPref.getCurrentUserId(); + + Account account = daoSession.getAccountDao().load(userId); + resetAccountRelations(account); + + List lastList = getUpdatedList(getComparedList(account)); + + daoSession.getGradeDao().deleteInTx(account.getGradeList()); + daoSession.getGradeDao().insertInTx(lastList); + + LogUtils.debug("Synchronization grades (amount = " + lastList.size() + ")"); + } + + private void resetAccountRelations(Account account) { + account.resetSubjectList(); + account.resetGradeList(); + } + + private List getUpdatedList(List comparedList) { + List updatedList = new ArrayList<>(); + + for (Grade grade : comparedList) { + grade.setUserId(userId); + grade.setSubjectId(getSubjectId(grade.getSubject())); + updatedList.add(grade); + } + return updatedList; + } + + private List getComparedList(Account account) throws IOException, VulcanException, + ParseException { + List gradesFromNet = DataObjectConverter + .gradesToGradeEntities(vulcan.getGradesList().getAll()); + + List gradesFromDb = account.getGradeList(); + + return EntitiesCompare.compareGradeList(gradesFromNet, gradesFromDb); + } + + private Long getSubjectId(String subjectName) { + return daoSession.getSubjectDao().queryBuilder() + .where(SubjectDao.Properties.Name.eq(subjectName), + SubjectDao.Properties.UserId.eq(userId)) + .build() + .uniqueOrThrow() + .getId(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/subjects/SubjectSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/subjects/SubjectSync.java new file mode 100644 index 000000000..1c5bfc710 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/subjects/SubjectSync.java @@ -0,0 +1,71 @@ +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.VulcanException; +import io.github.wulkanowy.data.db.dao.entities.Account; +import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.dao.entities.Subject; +import io.github.wulkanowy.data.db.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 DaoSession daoSession; + + private final Vulcan vulcan; + + private final SharedPrefContract sharedPref; + + private Long userId; + + @Inject + SubjectSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) { + this.daoSession = daoSession; + this.sharedPref = sharedPref; + this.vulcan = vulcan; + } + + @Override + public void sync() throws VulcanException, IOException, ParseException { + + userId = sharedPref.getCurrentUserId(); + + List lastList = getUpdatedList(getSubjectsFromNet()); + + daoSession.getSubjectDao().deleteInTx(getSubjectsFromDb()); + daoSession.getSubjectDao().insertInTx(lastList); + + LogUtils.debug("Synchronization subjects (amount = " + lastList.size() + ")"); + } + + private List getSubjectsFromNet() throws VulcanException, IOException { + return DataObjectConverter.subjectsToSubjectEntities(vulcan.getSubjectsList().getAll()); + } + + private List getSubjectsFromDb() { + Account account = daoSession.getAccountDao().load(userId); + account.resetSubjectList(); + return account.getSubjectList(); + } + + private List getUpdatedList(List subjectsFromNet) { + List updatedList = new ArrayList<>(); + + for (Subject subject : subjectsFromNet) { + subject.setUserId(userId); + updatedList.add(subject); + } + return updatedList; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java new file mode 100644 index 000000000..da6b143f2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java @@ -0,0 +1,162 @@ +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.VulcanException; +import io.github.wulkanowy.api.generic.Lesson; +import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.dao.entities.Day; +import io.github.wulkanowy.data.db.dao.entities.DayDao; +import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; +import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao; +import io.github.wulkanowy.data.db.dao.entities.Week; +import io.github.wulkanowy.data.db.dao.entities.WeekDao; +import io.github.wulkanowy.data.db.shared.SharedPrefContract; +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 SharedPrefContract sharedPref; + + private final Vulcan vulcan; + + private long userId; + + @Inject + TimetableSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) { + this.daoSession = daoSession; + this.sharedPref = sharedPref; + this.vulcan = vulcan; + } + + @Override + public void syncTimetable() throws IOException, ParseException, VulcanException { + syncTimetable(null); + } + + @Override + public void syncTimetable(String date) throws IOException, ParseException, VulcanException { + this.userId = sharedPref.getCurrentUserId(); + + io.github.wulkanowy.api.generic.Week weekApi = getWeekFromApi(getNormalizedDate(date)); + Week weekDb = getWeekFromDb(weekApi.getStartDayDate()); + + long weekId = updateWeekInDb(weekDb, weekApi); + + List lessonList = updateDays(weekApi.getDays(), weekId); + + daoSession.getTimetableLessonDao().saveInTx(lessonList); + + LogUtils.debug("Synchronization timetable lessons (amount = " + lessonList.size() + ")"); + } + + private String getNormalizedDate(String date) throws ParseException { + return null != date ? String.valueOf(TimeUtils.getNetTicks(date)) : ""; + } + + private io.github.wulkanowy.api.generic.Week getWeekFromApi(String date) + throws IOException, VulcanException, ParseException { + return vulcan.getTimetable().getWeekTable(date); + } + + private Week getWeekFromDb(String date) { + return daoSession.getWeekDao() + .queryBuilder() + .where(WeekDao.Properties.UserId.eq(userId), WeekDao.Properties.StartDayDate.eq(date)) + .unique(); + } + + private Long updateWeekInDb(Week dbEntity, io.github.wulkanowy.api.generic.Week fromApi) { + if (dbEntity != null) { + dbEntity.setIsTimetableSynced(true); + dbEntity.update(); + + return dbEntity.getId(); + } + + Week apiEntity = DataObjectConverter.weekToWeekEntity(fromApi).setUserId(userId); + apiEntity.setIsTimetableSynced(true); + + return daoSession.getWeekDao().insert(apiEntity); + } + + private List updateDays(List dayListFromApi, long weekId) { + List updatedLessonList = new ArrayList<>(); + + for (io.github.wulkanowy.api.generic.Day dayFromApi : dayListFromApi) { + + Day dbDayEntity = getDayFromDb(dayFromApi.getDate()); + + Day apiDayEntity = DataObjectConverter.dayToDayEntity(dayFromApi); + + long dayId = updateDay(dbDayEntity, apiDayEntity, weekId); + + updateLessons(dayFromApi.getLessons(), updatedLessonList, dayId); + } + + return updatedLessonList; + } + + private Day getDayFromDb(String date) { + return daoSession.getDayDao() + .queryBuilder() + .where(DayDao.Properties.UserId.eq(userId), DayDao.Properties.Date.eq(date)) + .unique(); + } + + private long updateDay(Day dayFromDb, Day apiDayEntity, long weekId) { + apiDayEntity.setUserId(userId); + apiDayEntity.setWeekId(weekId); + + if (null != dayFromDb) { + apiDayEntity.setId(dayFromDb.getId()); + + daoSession.getDayDao().save(apiDayEntity); + dayFromDb.refresh(); + + return dayFromDb.getId(); + } + + return daoSession.getDayDao().insert(apiDayEntity); + } + + private void updateLessons(List lessons, List updatedLessons, long dayId) { + List lessonsFromApiEntities = DataObjectConverter + .lessonsToTimetableLessonsEntities(lessons); + + for (TimetableLesson apiLessonEntity : lessonsFromApiEntities) { + TimetableLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId); + + apiLessonEntity.setDayId(dayId); + + if (lessonFromDb != null) { + apiLessonEntity.setId(lessonFromDb.getId()); + } + + if (!"".equals(apiLessonEntity.getSubject())) { + updatedLessons.add(apiLessonEntity); + } + } + } + + private TimetableLesson getLessonFromDb(TimetableLesson apiEntity, long dayId) { + return daoSession.getTimetableLessonDao().queryBuilder() + .where(TimetableLessonDao.Properties.DayId.eq(dayId), + TimetableLessonDao.Properties.Date.eq(apiEntity.getDate()), + TimetableLessonDao.Properties.StartTime.eq(apiEntity.getStartTime()), + TimetableLessonDao.Properties.EndTime.eq(apiEntity.getEndTime())) + .unique(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSyncContract.java b/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSyncContract.java new file mode 100644 index 000000000..9bba209a7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSyncContract.java @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.sync.timetable; + +import java.io.IOException; +import java.text.ParseException; + +import io.github.wulkanowy.api.VulcanException; + +public interface TimetableSyncContract { + + void syncTimetable(String date) throws VulcanException, IOException, ParseException; + + void syncTimetable() throws VulcanException, IOException, ParseException; +} diff --git a/app/src/main/java/io/github/wulkanowy/di/AppComponent.kt b/app/src/main/java/io/github/wulkanowy/di/AppComponent.kt deleted file mode 100644 index 9e8b24ab8..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/AppComponent.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.wulkanowy.di - -import dagger.Component -import dagger.android.AndroidInjector -import dagger.android.support.AndroidSupportInjectionModule -import io.github.wulkanowy.WulkanowyApp -import io.github.wulkanowy.data.RepositoryModule -import io.github.wulkanowy.services.ServicesModule -import javax.inject.Singleton - -@Singleton -@Component(modules = [ - AndroidSupportInjectionModule::class, - AppModule::class, - RepositoryModule::class, - ServicesModule::class, - BuilderModule::class]) -interface AppComponent : AndroidInjector { - - @Component.Builder - abstract class Builder : AndroidInjector.Builder() -} diff --git a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt deleted file mode 100644 index eb3b18f99..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.wulkanowy.di - -import android.appwidget.AppWidgetManager -import android.content.Context -import com.google.firebase.analytics.FirebaseAnalytics -import dagger.Module -import dagger.Provides -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.BuildConfig.DEBUG -import io.github.wulkanowy.WulkanowyApp -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import javax.inject.Named -import javax.inject.Singleton - -@Module -internal class AppModule { - - @Singleton - @Provides - fun provideContext(app: WulkanowyApp): Context = app - - @Singleton - @Provides - fun provideSchedulersProvider() = SchedulersProvider() - - @Provides - fun provideFlexibleAdapter() = FlexibleAdapter>(null, null, true) - - @Singleton - @Provides - fun provideFirebaseAnalyticsHelper(context: Context) = FirebaseAnalyticsHelper(FirebaseAnalytics.getInstance(context)) - - @Singleton - @Provides - fun provideAppWidgetManager(context: Context) = AppWidgetManager.getInstance(context) - - @Singleton - @Named("isDebug") - @Provides - fun provideIsDebug() = DEBUG -} diff --git a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt deleted file mode 100644 index 7f4776304..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.di - -import dagger.Module -import dagger.android.ContributesAndroidInjector -import io.github.wulkanowy.di.scopes.PerActivity -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.login.LoginModule -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainModule -import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity -import io.github.wulkanowy.ui.modules.splash.SplashActivity -import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetProvider - -@Module -internal abstract class BuilderModule { - - @PerActivity - @ContributesAndroidInjector - abstract fun bindSplashActivity(): SplashActivity - - @PerActivity - @ContributesAndroidInjector(modules = [LoginModule::class]) - abstract fun bindLoginActivity(): LoginActivity - - @PerActivity - @ContributesAndroidInjector(modules = [MainModule::class]) - abstract fun bindMainActivity(): MainActivity - - @ContributesAndroidInjector - abstract fun bindMessageSendActivity(): SendMessageActivity - - @ContributesAndroidInjector - abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider -} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/ActivityContext.java b/app/src/main/java/io/github/wulkanowy/di/annotations/ActivityContext.java new file mode 100644 index 000000000..2a74c32d3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/annotations/ActivityContext.java @@ -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 { +} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/ApplicationContext.java b/app/src/main/java/io/github/wulkanowy/di/annotations/ApplicationContext.java new file mode 100644 index 000000000..04139d998 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/annotations/ApplicationContext.java @@ -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 { +} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/DatabaseInfo.java b/app/src/main/java/io/github/wulkanowy/di/annotations/DatabaseInfo.java new file mode 100644 index 000000000..fabcefbaa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/annotations/DatabaseInfo.java @@ -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 { +} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/PerActivity.java b/app/src/main/java/io/github/wulkanowy/di/annotations/PerActivity.java new file mode 100644 index 000000000..f103994a5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/annotations/PerActivity.java @@ -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 { +} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/PerFragment.java b/app/src/main/java/io/github/wulkanowy/di/annotations/PerFragment.java new file mode 100644 index 000000000..98f364f76 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/annotations/PerFragment.java @@ -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 { +} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/SharedPreferencesInfo.java b/app/src/main/java/io/github/wulkanowy/di/annotations/SharedPreferencesInfo.java new file mode 100644 index 000000000..919c77a0b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/annotations/SharedPreferencesInfo.java @@ -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 { +} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/SyncGrades.java b/app/src/main/java/io/github/wulkanowy/di/annotations/SyncGrades.java new file mode 100644 index 000000000..90e6c02f6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/annotations/SyncGrades.java @@ -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 { +} diff --git a/app/src/main/java/io/github/wulkanowy/di/annotations/SyncSubjects.java b/app/src/main/java/io/github/wulkanowy/di/annotations/SyncSubjects.java new file mode 100644 index 000000000..81d351bea --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/annotations/SyncSubjects.java @@ -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 { +} diff --git a/app/src/main/java/io/github/wulkanowy/di/component/ActivityComponent.java b/app/src/main/java/io/github/wulkanowy/di/component/ActivityComponent.java new file mode 100644 index 000000000..3365a317e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/component/ActivityComponent.java @@ -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); +} diff --git a/app/src/main/java/io/github/wulkanowy/di/component/ApplicationComponent.java b/app/src/main/java/io/github/wulkanowy/di/component/ApplicationComponent.java new file mode 100644 index 000000000..3439a9496 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/component/ApplicationComponent.java @@ -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); +} diff --git a/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java b/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java new file mode 100644 index 000000000..b26abaaf3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java @@ -0,0 +1,28 @@ +package io.github.wulkanowy.di.component; + +import dagger.Component; +import io.github.wulkanowy.di.annotations.PerFragment; +import io.github.wulkanowy.di.modules.FragmentModule; +import io.github.wulkanowy.ui.main.attendance.AttendanceFragment; +import io.github.wulkanowy.ui.main.attendance.AttendanceTabFragment; +import io.github.wulkanowy.ui.main.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(AttendanceTabFragment attendanceTabFragment); + + void inject(DashboardFragment dashboardFragment); + + void inject(TimetableFragment timetableFragment); + + void inject(TimetableTabFragment timetableTabFragment); +} diff --git a/app/src/main/java/io/github/wulkanowy/di/modules/ActivityModule.java b/app/src/main/java/io/github/wulkanowy/di/modules/ActivityModule.java new file mode 100644 index 000000000..53dc10864 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/modules/ActivityModule.java @@ -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()); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java b/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java new file mode 100644 index 000000000..e7d2bcc67 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java @@ -0,0 +1,137 @@ +package io.github.wulkanowy.di.modules; + +import android.app.Application; +import android.content.Context; + +import com.firebase.jobdispatcher.FirebaseJobDispatcher; +import com.firebase.jobdispatcher.GooglePlayDriver; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; +import io.github.wulkanowy.api.Vulcan; +import io.github.wulkanowy.data.Repository; +import io.github.wulkanowy.data.RepositoryContract; +import io.github.wulkanowy.data.db.dao.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.account.AccountSync; +import io.github.wulkanowy.data.sync.account.AccountSyncContract; +import io.github.wulkanowy.data.sync.attendance.AttendanceSync; +import io.github.wulkanowy.data.sync.attendance.AttendanceSyncContract; +import io.github.wulkanowy.data.sync.grades.GradeSync; +import io.github.wulkanowy.data.sync.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 +public class ApplicationModule { + + private final Application application; + + public ApplicationModule(Application application) { + this.application = application; + } + + @Provides + Application provideApplication() { + return application; + } + + @ApplicationContext + @Provides + Context provideAppContext() { + return application; + } + + @DatabaseInfo + @Provides + String provideDatabaseName() { + return AppConstant.DATABASE_NAME; + } + + @SharedPreferencesInfo + @Provides + String provideSharedPreferencesName() { + return AppConstant.SHARED_PREFERENCES_NAME; + } + + @Singleton + @Provides + DaoSession provideDaoSession(DbHelper dbHelper) { + return new DaoMaster(dbHelper.getWritableDb()).newSession(); + } + + @Singleton + @Provides + Vulcan provideVulcan() { + return new Vulcan(); + } + + @Singleton + @Provides + RepositoryContract provideRepository(Repository repository) { + return repository; + } + + @Singleton + @Provides + SharedPrefContract provideSharedPref(SharedPref sharedPref) { + return sharedPref; + } + + @Singleton + @Provides + ResourcesContract provideAppResources(AppResources appResources) { + return appResources; + } + + @Singleton + @Provides + AccountSyncContract provideLoginSync(AccountSync accountSync) { + return accountSync; + } + + @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; + } + + @Singleton + @Provides + AttendanceSyncContract provideAttendanceSync(AttendanceSync attendanceSync) { + return attendanceSync; + } + + @Provides + FirebaseJobDispatcher provideDispatcher() { + return new FirebaseJobDispatcher(new GooglePlayDriver(application)); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java b/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java new file mode 100644 index 000000000..3479a8c4e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java @@ -0,0 +1,96 @@ +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.AttendanceHeaderItem; +import io.github.wulkanowy.ui.main.attendance.AttendancePagerAdapter; +import io.github.wulkanowy.ui.main.attendance.AttendancePresenter; +import io.github.wulkanowy.ui.main.attendance.AttendanceTabContract; +import io.github.wulkanowy.ui.main.attendance.AttendanceTabPresenter; +import io.github.wulkanowy.ui.main.dashboard.DashboardContract; +import io.github.wulkanowy.ui.main.dashboard.DashboardPresenter; +import io.github.wulkanowy.ui.main.grades.GradeHeaderItem; +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 + AttendanceTabContract.Presenter provideAttendanceTabPresenter(AttendanceTabPresenter timetableTabPresenter) { + return timetableTabPresenter; + } + + @Provides + AttendancePagerAdapter provideAttendancePagerAdapter() { + return new AttendancePagerAdapter(fragment.getChildFragmentManager()); + } + + @Provides + FlexibleAdapter provideAttendanceTabAdapter() { + return new FlexibleAdapter<>(null); + } + + @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 provideTimetableTabAdapter() { + return new FlexibleAdapter<>(null); + } + + @Provides + FlexibleAdapter provideGradesAdapter() { + return new FlexibleAdapter<>(null); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/di/scopes/PerActivity.kt b/app/src/main/java/io/github/wulkanowy/di/scopes/PerActivity.kt deleted file mode 100644 index c1b4352a4..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/scopes/PerActivity.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.wulkanowy.di.scopes - -import javax.inject.Scope - -@Scope -@Retention(AnnotationRetention.RUNTIME) -annotation class PerActivity diff --git a/app/src/main/java/io/github/wulkanowy/di/scopes/PerChildFragment.kt b/app/src/main/java/io/github/wulkanowy/di/scopes/PerChildFragment.kt deleted file mode 100644 index 08884dac5..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/scopes/PerChildFragment.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.wulkanowy.di.scopes - -import javax.inject.Scope - -@Scope -@Retention(AnnotationRetention.RUNTIME) -annotation class PerChildFragment diff --git a/app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.kt b/app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.kt deleted file mode 100644 index 1203d7d88..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.wulkanowy.di.scopes - -import javax.inject.Scope - -@Scope -@Retention(AnnotationRetention.RUNTIME) -annotation class PerFragment diff --git a/app/src/main/java/io/github/wulkanowy/services/NotificationService.java b/app/src/main/java/io/github/wulkanowy/services/NotificationService.java new file mode 100644 index 000000000..ab721f46c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/NotificationService.java @@ -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; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt deleted file mode 100644 index 84409a845..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt +++ /dev/null @@ -1,107 +0,0 @@ -package io.github.wulkanowy.services - -import android.app.NotificationManager -import android.content.Context -import android.content.Context.NOTIFICATION_SERVICE -import androidx.core.app.NotificationManagerCompat -import androidx.work.WorkManager -import com.squareup.inject.assisted.dagger2.AssistedModule -import dagger.Binds -import dagger.Module -import dagger.Provides -import dagger.android.ContributesAndroidInjector -import dagger.multibindings.IntoSet -import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork -import io.github.wulkanowy.services.sync.works.AttendanceWork -import io.github.wulkanowy.services.sync.works.CompletedLessonWork -import io.github.wulkanowy.services.sync.works.ExamWork -import io.github.wulkanowy.services.sync.works.GradeStatisticsWork -import io.github.wulkanowy.services.sync.works.GradeSummaryWork -import io.github.wulkanowy.services.sync.works.GradeWork -import io.github.wulkanowy.services.sync.works.HomeworkWork -import io.github.wulkanowy.services.sync.works.LuckyNumberWork -import io.github.wulkanowy.services.sync.works.MessageWork -import io.github.wulkanowy.services.sync.works.NoteWork -import io.github.wulkanowy.services.sync.works.RecipientWork -import io.github.wulkanowy.services.sync.works.TimetableWork -import io.github.wulkanowy.services.sync.works.Work -import io.github.wulkanowy.services.widgets.TimetableWidgetService -import javax.inject.Singleton - -@AssistedModule -@Module(includes = [AssistedInject_ServicesModule::class]) -abstract class ServicesModule { - - @Module - companion object { - - @JvmStatic - @Provides - fun provideWorkManager() = WorkManager.getInstance() - - @JvmStatic - @Singleton - @Provides - fun provideNotificationManagerCompat(context: Context) = NotificationManagerCompat.from(context) - - @JvmStatic - @Singleton - @Provides - fun provideNotificationManager(context: Context) = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager - } - - @ContributesAndroidInjector - abstract fun bindTimetableWidgetService(): TimetableWidgetService - - @Binds - @IntoSet - abstract fun provideGradeWork(work: GradeWork): Work - - @Binds - @IntoSet - abstract fun provideNoteWork(work: NoteWork): Work - - @Binds - @IntoSet - abstract fun provideAttendanceWork(work: AttendanceWork): Work - - @Binds - @IntoSet - abstract fun provideGradeSummaryWork(work: GradeSummaryWork): Work - - @Binds - @IntoSet - abstract fun provideExamWork(work: ExamWork): Work - - @Binds - @IntoSet - abstract fun provideAttendanceSummaryWork(work: AttendanceSummaryWork): Work - - @Binds - @IntoSet - abstract fun provideTimetableWork(work: TimetableWork): Work - - @Binds - @IntoSet - abstract fun provideLuckyNumberWork(work: LuckyNumberWork): Work - - @Binds - @IntoSet - abstract fun provideCompletedLessonWork(work: CompletedLessonWork): Work - - @Binds - @IntoSet - abstract fun provideHomeworkWork(work: HomeworkWork): Work - - @Binds - @IntoSet - abstract fun provideMessageWork(work: MessageWork): Work - - @Binds - @IntoSet - abstract fun provideRecipientWork(work: RecipientWork): Work - - @Binds - @IntoSet - abstract fun provideGradeStatistics(work: GradeStatisticsWork): Work -} diff --git a/app/src/main/java/io/github/wulkanowy/services/SyncJob.java b/app/src/main/java/io/github/wulkanowy/services/SyncJob.java new file mode 100644 index 000000000..e7b0908f0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/SyncJob.java @@ -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 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.initLastUser(); + 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_stat_notify) + .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()); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt deleted file mode 100644 index c1820afb9..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.services.sync - -import android.os.Build.VERSION.SDK_INT -import android.os.Build.VERSION_CODES.O -import androidx.work.BackoffPolicy.EXPONENTIAL -import androidx.work.Constraints -import androidx.work.ExistingPeriodicWorkPolicy.KEEP -import androidx.work.ExistingPeriodicWorkPolicy.REPLACE -import androidx.work.NetworkType.METERED -import androidx.work.NetworkType.UNMETERED -import androidx.work.PeriodicWorkRequest -import androidx.work.WorkManager -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.services.sync.channels.DebugChannel -import io.github.wulkanowy.services.sync.channels.NewEntriesChannel -import io.github.wulkanowy.utils.isHolidays -import org.threeten.bp.LocalDate.now -import timber.log.Timber -import java.util.concurrent.TimeUnit.MINUTES -import javax.inject.Inject -import javax.inject.Named -import javax.inject.Singleton - -@Singleton -class SyncManager @Inject constructor( - private val workManager: WorkManager, - private val preferencesRepository: PreferencesRepository, - newEntriesChannel: NewEntriesChannel, - debugChannel: DebugChannel, - @Named("isDebug") isDebug: Boolean -) { - - init { - if (SDK_INT >= O) newEntriesChannel.create() - if (SDK_INT >= O && isDebug) debugChannel.create() - if (now().isHolidays) stopSyncWorker() - Timber.i("SyncManager was initialized") - } - - fun startSyncWorker(restart: Boolean = false) { - if (preferencesRepository.isServiceEnabled && !now().isHolidays) { - workManager.enqueueUniquePeriodicWork(SyncWorker::class.java.simpleName, if (restart) REPLACE else KEEP, - PeriodicWorkRequest.Builder(SyncWorker::class.java, preferencesRepository.servicesInterval, MINUTES, 10, MINUTES) - .setBackoffCriteria(EXPONENTIAL, 30, MINUTES) - .setConstraints(Constraints.Builder() - .setRequiredNetworkType(if (preferencesRepository.isServicesOnlyWifi) METERED else UNMETERED) - .build()) - .build()) - } - } - - fun stopSyncWorker() { - workManager.cancelUniqueWork(SyncWorker::class.java.simpleName) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt deleted file mode 100644 index 134fb0344..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt +++ /dev/null @@ -1,81 +0,0 @@ -package io.github.wulkanowy.services.sync - -import android.content.Context -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationCompat.BigTextStyle -import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT -import androidx.core.app.NotificationManagerCompat -import androidx.work.ListenableWorker -import androidx.work.RxWorker -import androidx.work.WorkerParameters -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject -import io.github.wulkanowy.R -import io.github.wulkanowy.api.interceptor.FeatureDisabledException -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.services.sync.channels.DebugChannel -import io.github.wulkanowy.services.sync.works.Work -import io.github.wulkanowy.utils.getCompatColor -import io.reactivex.Completable -import io.reactivex.Single -import timber.log.Timber -import kotlin.random.Random - -class SyncWorker @AssistedInject constructor( - @Assisted appContext: Context, - @Assisted workerParameters: WorkerParameters, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val works: Set<@JvmSuppressWildcards Work>, - private val preferencesRepository: PreferencesRepository, - private val notificationManager: NotificationManagerCompat -) : RxWorker(appContext, workerParameters) { - - override fun createWork(): Single { - Timber.i("SyncWorker is starting") - return studentRepository.isStudentSaved() - .filter { true } - .flatMap { studentRepository.getCurrentStudent().toMaybe() } - .flatMapCompletable { student -> - semesterRepository.getCurrentSemester(student, true) - .flatMapCompletable { semester -> - Completable.mergeDelayError(works.map { work -> - work.create(student, semester) - .doOnSubscribe { Timber.i("${work::class.java.simpleName} is starting") } - .doOnError { Timber.i("${work::class.java.simpleName} result: An exception occurred") } - .doOnComplete { Timber.i("${work::class.java.simpleName} result: Success") } - }) - } - } - .toSingleDefault(Result.success()) - .onErrorReturn { - Timber.e(it, "There was an error during synchronization") - if (it is FeatureDisabledException) Result.success() - else Result.retry() - } - .doOnSuccess { - if (preferencesRepository.isDebugNotificationEnable) notify(it) - Timber.i("SyncWorker result: $it") - } - } - - private fun notify(result: Result) { - notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(applicationContext, DebugChannel.CHANNEL_ID) - .setContentTitle("Debug notification") - .setSmallIcon(R.drawable.ic_more_settings_24dp) - .setAutoCancel(true) - .setColor(applicationContext.getCompatColor(R.color.colorPrimary)) - .setStyle(BigTextStyle().bigText("${SyncWorker::class.java.simpleName} result: $result")) - .setPriority(PRIORITY_DEFAULT) - .build()) - } - - @AssistedInject.Factory - interface Factory { - - fun create(appContext: Context, workerParameters: WorkerParameters): ListenableWorker - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorkerFactory.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorkerFactory.kt deleted file mode 100644 index aadfc27f4..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorkerFactory.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.services.sync - -import android.content.Context -import androidx.work.ListenableWorker -import androidx.work.WorkerFactory -import androidx.work.WorkerParameters -import timber.log.Timber -import javax.inject.Inject - -class SyncWorkerFactory @Inject constructor(private val syncWorkerFactory: SyncWorker.Factory) : WorkerFactory() { - - override fun createWorker(appContext: Context, workerClassName: String, workerParameters: WorkerParameters): ListenableWorker? { - return if (workerClassName == SyncWorker::class.java.name) { - syncWorkerFactory.create(appContext, workerParameters) - } else { - Timber.e(IllegalArgumentException("Unknown worker class name: $workerClassName")) - null - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt deleted file mode 100644 index 02fff5690..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.services.sync.channels - -import android.annotation.TargetApi -import android.app.Notification.VISIBILITY_PUBLIC -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.NotificationManager.IMPORTANCE_DEFAULT -import android.content.Context -import io.github.wulkanowy.R -import javax.inject.Inject - -@TargetApi(26) -class DebugChannel @Inject constructor( - private val notificationManager: NotificationManager, - private val context: Context -) { - - companion object { - const val CHANNEL_ID = "debug_channel" - } - - fun create() { - notificationManager.createNotificationChannel( - NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_debug), IMPORTANCE_DEFAULT).apply { - lockscreenVisibility = VISIBILITY_PUBLIC - }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewEntriesChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewEntriesChannel.kt deleted file mode 100644 index 8e24a2a67..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewEntriesChannel.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.services.sync.channels - -import android.annotation.TargetApi -import android.app.Notification.VISIBILITY_PUBLIC -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.NotificationManager.IMPORTANCE_HIGH -import android.content.Context -import io.github.wulkanowy.R -import javax.inject.Inject - -@TargetApi(26) -class NewEntriesChannel @Inject constructor( - private val notificationManager: NotificationManager, - private val context: Context -) { - - companion object { - const val CHANNEL_ID = "new_entries_channel" - } - - fun create() { - notificationManager.createNotificationChannel( - NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_entries), IMPORTANCE_HIGH).apply { - enableLights(true) - enableVibration(true) - lockscreenVisibility = VISIBILITY_PUBLIC - }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt deleted file mode 100644 index 01978c5b6..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.attendancesummary.AttendanceSummaryRepository -import io.reactivex.Completable -import javax.inject.Inject - -class AttendanceSummaryWork @Inject constructor( - private val attendanceSummaryRepository: AttendanceSummaryRepository -) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return attendanceSummaryRepository.getAttendanceSummary(semester, -1, true).ignoreElement() - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt deleted file mode 100644 index e4b55b0ea..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Completable -import org.threeten.bp.LocalDate.now -import javax.inject.Inject - -class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return attendanceRepository.getAttendance(semester, now().monday, now().friday, true) - .ignoreElement() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt deleted file mode 100644 index 29642ad6f..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Completable -import org.threeten.bp.LocalDate.now -import javax.inject.Inject - -class CompletedLessonWork @Inject constructor( - private val completedLessonsRepository: CompletedLessonsRepository -) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return completedLessonsRepository.getCompletedLessons(semester, now().monday, now().friday, true) - .ignoreElement() - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt deleted file mode 100644 index 8744fcc79..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.exam.ExamRepository -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Completable -import org.threeten.bp.LocalDate.now -import javax.inject.Inject - -class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return examRepository.getExams(semester, now().monday, now().friday, true).ignoreElement() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt deleted file mode 100644 index 1de39a95f..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.gradestatistics.GradeStatisticsRepository -import io.reactivex.Completable -import javax.inject.Inject - -class GradeStatisticsWork @Inject constructor(private val gradeStatisticsRepository: GradeStatisticsRepository) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return gradeStatisticsRepository.getGradesStatistics(semester, "Wszystkie", false, true) - .ignoreElement() - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt deleted file mode 100644 index 6de0bc5b0..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository -import io.reactivex.Completable -import javax.inject.Inject - -class GradeSummaryWork @Inject constructor(private val gradeSummaryRepository: GradeSummaryRepository) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return gradeSummaryRepository.getGradesSummary(semester, true).ignoreElement() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt deleted file mode 100644 index 66316934a..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt +++ /dev/null @@ -1,61 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.content.Context -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationCompat.DEFAULT_ALL -import androidx.core.app.NotificationCompat.PRIORITY_HIGH -import androidx.core.app.NotificationManagerCompat -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.grade.GradeRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.services.sync.channels.NewEntriesChannel -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX -import io.github.wulkanowy.utils.getCompatColor -import io.reactivex.Completable -import javax.inject.Inject -import kotlin.random.Random - -class GradeWork @Inject constructor( - private val context: Context, - private val notificationManager: NotificationManagerCompat, - private val gradeRepository: GradeRepository, - private val preferencesRepository: PreferencesRepository -) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return gradeRepository.getGrades(student, semester, true, preferencesRepository.isNotificationsEnable) - .flatMap { gradeRepository.getNotNotifiedGrades(semester) } - .flatMapCompletable { - if (it.isNotEmpty()) notify(it) - gradeRepository.updateGrades(it.onEach { grade -> grade.isNotified = true }) - } - } - - private fun notify(grades: List) { - notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID) - .setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items, grades.size, grades.size)) - .setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items, grades.size, grades.size)) - .setSmallIcon(R.drawable.ic_stat_notify_grade) - .setAutoCancel(true) - .setPriority(PRIORITY_HIGH) - .setDefaults(DEFAULT_ALL) - .setColor(context.getCompatColor(R.color.colorPrimary)) - .setContentIntent( - PendingIntent.getActivity(context, 0, - MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 0), FLAG_UPDATE_CURRENT)) - .setStyle(NotificationCompat.InboxStyle().run { - setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, grades.size, grades.size)) - grades.forEach { addLine("${it.subject}: ${it.entry}") } - this - }) - .build() - ) - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt deleted file mode 100644 index 32b356c68..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.homework.HomeworkRepository -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Completable -import org.threeten.bp.LocalDate.now -import javax.inject.Inject - -class HomeworkWork @Inject constructor(private val homeworkRepository: HomeworkRepository) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return homeworkRepository.getHomework(semester, now().monday, now().friday, true).ignoreElement() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt deleted file mode 100644 index ca7aaac60..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.content.Context -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationCompat.DEFAULT_ALL -import androidx.core.app.NotificationCompat.PRIORITY_HIGH -import androidx.core.app.NotificationManagerCompat -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.services.sync.channels.NewEntriesChannel -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX -import io.github.wulkanowy.utils.getCompatColor -import io.reactivex.Completable -import javax.inject.Inject -import kotlin.random.Random - -class LuckyNumberWork @Inject constructor( - private val context: Context, - private val notificationManager: NotificationManagerCompat, - private val luckyNumberRepository: LuckyNumberRepository, - private val preferencesRepository: PreferencesRepository -) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return luckyNumberRepository.getLuckyNumber(semester, true, preferencesRepository.isNotificationsEnable) - .flatMap { luckyNumberRepository.getNotNotifiedLuckyNumber(semester) } - .flatMapCompletable { - notify(it) - luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true }) - } - } - - private fun notify(luckyNumber: LuckyNumber) { - notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID) - .setContentTitle(context.getString(R.string.lucky_number_notify_new_item_title)) - .setContentText(context.getString(R.string.lucky_number_notify_new_item, luckyNumber.luckyNumber)) - .setSmallIcon(R.drawable.ic_stat_notify_lucky_number) - .setAutoCancel(true) - .setDefaults(DEFAULT_ALL) - .setPriority(PRIORITY_HIGH) - .setColor(context.getCompatColor(R.color.colorPrimary)) - .setContentIntent( - PendingIntent.getActivity(context, 0, - MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT) - ) - .build()) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt deleted file mode 100644 index 7ab2c8d4b..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt +++ /dev/null @@ -1,61 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.content.Context -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationCompat.DEFAULT_ALL -import androidx.core.app.NotificationCompat.PRIORITY_HIGH -import androidx.core.app.NotificationManagerCompat -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED -import io.github.wulkanowy.data.repositories.message.MessageRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.services.sync.channels.NewEntriesChannel -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX -import io.github.wulkanowy.utils.getCompatColor -import io.reactivex.Completable -import javax.inject.Inject -import kotlin.random.Random - -class MessageWork @Inject constructor( - private val context: Context, - private val notificationManager: NotificationManagerCompat, - private val messageRepository: MessageRepository, - private val preferencesRepository: PreferencesRepository -) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return messageRepository.getMessages(student, RECEIVED, true, preferencesRepository.isNotificationsEnable) - .flatMap { messageRepository.getNotNotifiedMessages(student) } - .flatMapCompletable { - if (it.isNotEmpty()) notify(it) - messageRepository.updateMessages(it.onEach { message -> message.isNotified = true }) - } - } - - private fun notify(messages: List) { - notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID) - .setContentTitle(context.resources.getQuantityString(R.plurals.message_new_items, messages.size, messages.size)) - .setContentText(context.resources.getQuantityString(R.plurals.message_notify_new_items, messages.size, messages.size)) - .setSmallIcon(R.drawable.ic_stat_notify_message) - .setAutoCancel(true) - .setDefaults(DEFAULT_ALL) - .setPriority(PRIORITY_HIGH) - .setColor(context.getCompatColor(R.color.colorPrimary)) - .setContentIntent( - PendingIntent.getActivity(context, 0, - MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT) - ) - .setStyle(NotificationCompat.InboxStyle().run { - setSummaryText(context.resources.getQuantityString(R.plurals.message_number_item, messages.size, messages.size)) - messages.forEach { addLine("${it.sender}: ${it.subject}") } - this - }) - .build()) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt deleted file mode 100644 index d0e8fe21c..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt +++ /dev/null @@ -1,61 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.content.Context -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationCompat.DEFAULT_ALL -import androidx.core.app.NotificationCompat.PRIORITY_HIGH -import androidx.core.app.NotificationManagerCompat -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.note.NoteRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.services.sync.channels.NewEntriesChannel -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX -import io.github.wulkanowy.utils.getCompatColor -import io.reactivex.Completable -import javax.inject.Inject -import kotlin.random.Random - -class NoteWork @Inject constructor( - private val context: Context, - private val notificationManager: NotificationManagerCompat, - private val noteRepository: NoteRepository, - private val preferencesRepository: PreferencesRepository -) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return noteRepository.getNotes(student, semester, true, preferencesRepository.isNotificationsEnable) - .flatMap { noteRepository.getNotNotifiedNotes(student) } - .flatMapCompletable { - if (it.isNotEmpty()) notify(it) - noteRepository.updateNotes(it.onEach { note -> note.isNotified = true }) - } - } - - private fun notify(notes: List) { - notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID) - .setContentTitle(context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size)) - .setContentText(context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size)) - .setSmallIcon(R.drawable.ic_stat_notify_note) - .setAutoCancel(true) - .setDefaults(DEFAULT_ALL) - .setPriority(PRIORITY_HIGH) - .setColor(context.getCompatColor(R.color.colorPrimary)) - .setContentIntent( - PendingIntent.getActivity(context, 0, - MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 4), FLAG_UPDATE_CURRENT) - ) - .setStyle(NotificationCompat.InboxStyle().run { - setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size)) - notes.forEach { addLine("${it.teacher}: ${it.category}") } - this - }) - .build()) - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt deleted file mode 100644 index fa610dee4..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.recipient.RecipientRepository -import io.github.wulkanowy.data.repositories.reportingunit.ReportingUnitRepository -import io.reactivex.Completable -import javax.inject.Inject - -class RecipientWork @Inject constructor( - private val reportingUnitRepository: ReportingUnitRepository, - private val recipientRepository: RecipientRepository -) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return reportingUnitRepository.getReportingUnits(student, true) - .flatMapCompletable { units -> - Completable.mergeDelayError(units.map { - recipientRepository.getRecipients(student, 2, it, true).ignoreElement() - }) - } - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt deleted file mode 100644 index 743ae0e83..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.timetable.TimetableRepository -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Completable -import org.threeten.bp.LocalDate.now -import javax.inject.Inject - -class TimetableWork @Inject constructor(private val timetableRepository: TimetableRepository) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return timetableRepository.getTimetable(semester, now().monday, now().friday, true) - .ignoreElement() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt deleted file mode 100644 index 1601a103b..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Completable - -interface Work { - - fun create(student: Student, semester: Semester): Completable -} - diff --git a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt deleted file mode 100644 index 0432ee146..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.services.widgets - -import android.content.Intent -import android.widget.RemoteViewsService -import dagger.android.AndroidInjection -import io.github.wulkanowy.data.db.SharedPrefHelper -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.timetable.TimetableRepository -import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetFactory -import io.github.wulkanowy.utils.SchedulersProvider -import javax.inject.Inject - -class TimetableWidgetService : RemoteViewsService() { - - @Inject - lateinit var timetableRepo: TimetableRepository - - @Inject - lateinit var studentRepo: StudentRepository - - @Inject - lateinit var semesterRepo: SemesterRepository - - @Inject - lateinit var sharedPref: SharedPrefHelper - - @Inject - lateinit var schedulers: SchedulersProvider - - override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory { - AndroidInjection.inject(this) - return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, sharedPref, schedulers, applicationContext, intent) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.java b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.java new file mode 100644 index 000000000..c5c66df0c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.java @@ -0,0 +1,70 @@ +package io.github.wulkanowy.ui.base; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.app.AppCompatDelegate; +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); + + AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); + + 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; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt deleted file mode 100644 index f227dc19a..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.ui.base - -import android.os.Bundle -import android.view.View -import androidx.appcompat.app.AppCompatDelegate -import com.google.android.material.snackbar.Snackbar -import com.google.android.material.snackbar.Snackbar.LENGTH_LONG -import dagger.android.support.DaggerAppCompatActivity -import io.github.wulkanowy.R - -abstract class BaseActivity : DaggerAppCompatActivity(), BaseView { - - protected lateinit var messageContainer: View - - public override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) - } - - override fun showError(text: String, error: Throwable) { - Snackbar.make(messageContainer, text, LENGTH_LONG).setAction(R.string.all_details) { - ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString()) - }.show() - } - - override fun showMessage(text: String) { - Snackbar.make(messageContainer, text, LENGTH_LONG).show() - } - - override fun onDestroy() { - super.onDestroy() - invalidateOptionsMenu() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseContract.java b/app/src/main/java/io/github/wulkanowy/ui/base/BaseContract.java new file mode 100644 index 000000000..2a4dc5696 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseContract.java @@ -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 { + + void onStart(V view); + + void onDestroy(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.java b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.java new file mode 100644 index 000000000..efb9d61a9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.java @@ -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 + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt deleted file mode 100644 index 6e863c511..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.ui.base - -import android.view.View -import com.google.android.material.snackbar.Snackbar -import dagger.android.support.DaggerFragment -import io.github.wulkanowy.R - -abstract class BaseFragment : DaggerFragment(), BaseView { - - protected var messageContainer: View? = null - - override fun showError(text: String, error: Throwable) { - if (messageContainer == null) (activity as? BaseActivity)?.showError(text, error) - else messageContainer?.also { - Snackbar.make(it, text, Snackbar.LENGTH_LONG).setAction(R.string.all_details) { - ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) - }.show() - } - } - - override fun showMessage(text: String) { - if (messageContainer == null) (activity as? BaseActivity)?.showMessage(text) - else messageContainer?.also { Snackbar.make(it, text, Snackbar.LENGTH_LONG).show() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt deleted file mode 100644 index fcf7e9809..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.ui.base - -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentPagerAdapter - -class BaseFragmentPagerAdapter(private val fragmentManager: FragmentManager) : FragmentPagerAdapter(fragmentManager) { - - private val pages = mutableMapOf() - - var containerId = 0 - - fun getFragmentInstance(position: Int): Fragment? { - if (containerId == 0) throw IllegalArgumentException("Container id is 0") - return fragmentManager.findFragmentByTag("android:switcher:$containerId:$position") - } - - fun addFragments(fragments: List) { - fragments.forEach { pages[it] = null } - } - - fun addFragmentsWithTitle(pages: Map) { - this.pages.putAll(pages) - } - - override fun getItem(position: Int) = pages.keys.elementAt(position) - - override fun getCount() = pages.size - - override fun getPageTitle(position: Int) = pages.values.elementAt(position) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.java b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.java new file mode 100644 index 000000000..611b7a29e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.java @@ -0,0 +1,39 @@ +package io.github.wulkanowy.ui.base; + +import javax.inject.Inject; + +import io.github.wulkanowy.data.RepositoryContract; + +public class BasePresenter implements BaseContract.Presenter { + + 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; + } + + protected boolean isViewAttached() { + return view != null; + } + + public final RepositoryContract getRepository() { + return repository; + } + + public V getView() { + return view; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt deleted file mode 100644 index c5a31958d..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.ui.base - -import io.reactivex.disposables.CompositeDisposable - -open class BasePresenter(private val errorHandler: ErrorHandler) { - - val disposable = CompositeDisposable() - - var view: T? = null - - open fun onAttachView(view: T) { - this.view = view - errorHandler.showErrorMessage = { text, error -> view.showError(text, error) } - } - - open fun onDetachView() { - view = null - disposable.clear() - errorHandler.clear() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt deleted file mode 100644 index d2470fd17..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.wulkanowy.ui.base - -interface BaseView { - - fun showError(text: String, error: Throwable) - - fun showMessage(text: String) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt deleted file mode 100644 index db3918b90..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.ui.base - -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context.CLIPBOARD_SERVICE -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import android.widget.Toast.LENGTH_LONG -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import kotlinx.android.synthetic.main.dialog_error.* -import java.io.PrintWriter -import java.io.StringWriter - -class ErrorDialog : DialogFragment() { - - private lateinit var error: Throwable - - companion object { - private const val ARGUMENT_KEY = "Data" - - fun newInstance(error: Throwable): ErrorDialog { - return ErrorDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, error) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - error = getSerializable(ARGUMENT_KEY) as Throwable - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_error, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - StringWriter().let { writer -> - error.printStackTrace(PrintWriter(writer)) - - errorDialogContent.text = writer.toString() - errorDialogCopy.setOnClickListener { - ClipData.newPlainText("wulkanowyError", writer.toString()).let { clip -> - (activity?.getSystemService(CLIPBOARD_SERVICE) as? ClipboardManager)?.primaryClip = clip - } - Toast.makeText(context, R.string.all_copied, LENGTH_LONG).show() - } - } - errorDialogCancel.setOnClickListener { dismiss() } - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt deleted file mode 100644 index 83cbf0ef0..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.ui.base - -import android.content.res.Resources -import com.readystatesoftware.chuck.api.ChuckCollector -import io.github.wulkanowy.R -import io.github.wulkanowy.api.interceptor.FeatureDisabledException -import io.github.wulkanowy.api.interceptor.ServiceUnavailableException -import io.github.wulkanowy.api.login.NotLoggedInException -import timber.log.Timber -import java.net.SocketTimeoutException -import java.net.UnknownHostException -import javax.inject.Inject - -open class ErrorHandler @Inject constructor(protected val resources: Resources, private val chuckCollector: ChuckCollector) { - - var showErrorMessage: (String, Throwable) -> Unit = { _, _ -> } - - fun dispatch(error: Throwable) { - chuckCollector.onError(error.javaClass.simpleName, error) - Timber.e(error, "An exception occurred while the Wulkanowy was running") - proceed(error) - } - - protected open fun proceed(error: Throwable) { - showErrorMessage((when (error) { - is UnknownHostException -> resources.getString(R.string.error_no_internet) - is SocketTimeoutException -> resources.getString(R.string.error_timeout) - is NotLoggedInException -> resources.getString(R.string.error_login_failed) - is ServiceUnavailableException -> resources.getString(R.string.error_service_unavailable) - is FeatureDisabledException -> resources.getString(R.string.error_feature_disabled) - else -> resources.getString(R.string.error_unknown) - }), error) - } - - open fun clear() { - showErrorMessage = { _, _ -> } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt deleted file mode 100644 index 110729d4f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.ui.base.session - -import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK -import android.content.Intent.FLAG_ACTIVITY_NEW_TASK -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.main.MainActivity - -open class BaseSessionFragment : BaseFragment(), BaseSessionView { - - override fun showExpiredDialog() { - (activity as? MainActivity)?.showExpiredDialog() - } - - override fun openLoginView() { - activity?.also { - startActivity(LoginActivity.getStartIntent(it) - .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) }) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt deleted file mode 100644 index 2c9f50ea9..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt +++ /dev/null @@ -1,15 +0,0 @@ -package io.github.wulkanowy.ui.base.session - -import io.github.wulkanowy.ui.base.BasePresenter - -open class BaseSessionPresenter(private val errorHandler: SessionErrorHandler) : - BasePresenter(errorHandler) { - - override fun onAttachView(view: T) { - super.onAttachView(view) - errorHandler.apply { - onDecryptionFail = { view.showExpiredDialog() } - onNoCurrentStudent = { view.openLoginView() } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt deleted file mode 100644 index 619c2863a..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.ui.base.session - -import io.github.wulkanowy.ui.base.BaseView - -interface BaseSessionView : BaseView { - - fun showExpiredDialog() - - fun openLoginView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt deleted file mode 100644 index 017eabbab..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.ui.base.session - -import android.content.res.Resources -import com.readystatesoftware.chuck.api.ChuckCollector -import io.github.wulkanowy.data.exceptions.NoCurrentStudentException -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.security.ScramblerException -import javax.inject.Inject - -open class SessionErrorHandler @Inject constructor( - resources: Resources, - chuckCollector: ChuckCollector -) : ErrorHandler(resources, chuckCollector) { - - var onDecryptionFail: () -> Unit = {} - - var onNoCurrentStudent: () -> Unit = {} - - override fun proceed(error: Throwable) { - when (error) { - is ScramblerException -> onDecryptionFail() - is NoCurrentStudentException -> onNoCurrentStudent() - else -> super.proceed(error) - } - } - - override fun clear() { - super.clear() - onDecryptionFail = {} - onNoCurrentStudent = {} - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java new file mode 100644 index 000000000..95c5548b2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java @@ -0,0 +1,221 @@ +package io.github.wulkanowy.ui.login; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.design.widget.Snackbar; +import android.support.design.widget.TextInputLayout; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.EditText; +import android.widget.TextView; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import butterknife.OnEditorAction; +import io.github.wulkanowy.R; +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; + +public class LoginActivity extends BaseActivity implements LoginContract.View { + + @BindView(R.id.login_activity_email_edit) + EditText emailView; + + @BindView(R.id.login_activity_pass_edit) + EditText passwordView; + + @BindView(R.id.login_activity_symbol_edit) + AutoCompleteTextView symbolView; + + @BindView(R.id.login_activity_form_scroll) + View loginFormView; + + @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 + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_login); + + setButterKnife(ButterKnife.bind(this)); + getActivityComponent().inject(this); + + presenter.onStart(this); + + setUpOnCreate(); + + } + + protected void setUpOnCreate() { + symbolView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, + getResources().getStringArray(R.array.symbols))); + } + + @OnClick(R.id.login_activity_sign_button) + void onLoginButtonClick() { + presenter.attemptLogin( + 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) { + onLoginButtonClick(); + return true; + } + return false; + } + + @OnClick(R.id.login_activity_create_text) + void onCreateAccountButtonClick() { + CommonUtils.openInternalBrowserViewer(this, AppConstant.VULCAN_CREATE_ACCOUNT_URL); + } + + @OnClick(R.id.login_activity_forgot_text) + void onForgotPasswordButtonClick() { + CommonUtils.openInternalBrowserViewer(this, AppConstant.VULCAN_FORGOT_PASS_URL); + } + + @Override + public void setErrorEmailRequired() { + emailView.requestFocus(); + emailView.setError(getString(R.string.error_field_required)); + requestedView = emailView; + } + + @Override + public void setErrorEmailInvalid() { + emailView.requestFocus(); + emailView.setError(getString(R.string.error_invalid_email)); + requestedView = emailView; + } + + @Override + public void setErrorPassRequired() { + passwordView.requestFocus(); + passwordView.setError(getString(R.string.error_field_required)); + 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); + passwordView.setError(null); + } + + @Override + public void showSoftInput() { + KeyboardUtils.showSoftInput(requestedView, this); + } + + @Override + public void hideSoftInput() { + 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)); + } + + @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 + public void onDestroy() { + super.onDestroy(); + presenter.onDestroy(); + } + + private void onLoginProgressUpdate(String step, String message) { + loginProgressText.setText(String.format("%1$s/2 - %2$s...", step, message)); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginContract.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginContract.java new file mode 100644 index 000000000..debfbbde1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginContract.java @@ -0,0 +1,55 @@ +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 { + + void attemptLogin(String email, String password, String symbol); + + void onStartAsync(); + + void onDoInBackground(int stepNumber) throws Exception; + + void onLoginProgress(int step); + + void onEndAsync(boolean success, Exception exception); + + void onCanceledAsync(); + + RepositoryContract getRepository(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginPresenter.java new file mode 100644 index 000000000..2b6d173d0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginPresenter.java @@ -0,0 +1,165 @@ +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 + implements LoginContract.Presenter { + + private LoginTask loginAsync; + + private String email; + + private String password; + + private String symbol; + + @Inject + LoginPresenter(RepositoryContract repository) { + super(repository); + } + + @Override + public void attemptLogin(String email, String password, String symbol) { + getView().resetViewErrors(); + + this.email = email; + this.password = password; + this.symbol = getNormalizedSymbol(symbol); + + if (!isAllFieldCorrect(password, email)) { + getView().showSoftInput(); + return; + } + + if (getView().isNetworkConnected()) { + loginAsync = new LoginTask(this); + loginAsync.execute(); + + } else { + getView().onNoNetworkError(); + } + + getView().hideSoftInput(); + } + + @Override + public void onStartAsync() { + if (isViewAttached()) { + getView().showLoginProgress(true); + } + } + + @Override + public void onDoInBackground(int stepNumber) throws Exception { + switch (stepNumber) { + case 1: + getRepository().registerUser(email, password, symbol); + break; + case 2: + getRepository().syncAll(); + break; + } + } + + @Override + 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() { + if (isViewAttached()) { + 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 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; + } + + @Override + public void onDestroy() { + if (loginAsync != null) { + loginAsync.cancel(true); + loginAsync = null; + } + super.onDestroy(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginTask.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginTask.java new file mode 100644 index 000000000..b22194da9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginTask.java @@ -0,0 +1,49 @@ +package io.github.wulkanowy.ui.login; + +import android.os.AsyncTask; + +public class LoginTask extends AsyncTask { + + private LoginContract.Presenter presenter; + + private Exception exception; + + LoginTask(LoginContract.Presenter presenter) { + this.presenter = presenter; + } + + @Override + protected void onPreExecute() { + presenter.onStartAsync(); + } + + @Override + protected Boolean doInBackground(Void... params) { + try { + publishProgress(1); + presenter.onDoInBackground(1); + + publishProgress(2); + presenter.onDoInBackground(2); + } catch (Exception e) { + exception = e; + return false; + } + return true; + } + + @Override + protected void onProgressUpdate(Integer... progress) { + presenter.onLoginProgress(progress[0]); + } + + @Override + protected void onPostExecute(Boolean success) { + presenter.onEndAsync(success, exception); + } + + @Override + protected void onCancelled() { + presenter.onCanceledAsync(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java new file mode 100644 index 000000000..cc9b1dc8a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java @@ -0,0 +1,154 @@ +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.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; + +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.ic_menu_grade_26dp) + )); + bottomNavigation.addItem(new AHBottomNavigationItem( + getString(R.string.attendance_text), + getResources().getDrawable(R.drawable.ic_menu_attendance_24dp) + )); + bottomNavigation.addItem(new AHBottomNavigationItem( + getString(R.string.dashboard_text), + getResources().getDrawable(R.drawable.ic_menu_dashboard_24dp) + )); + bottomNavigation.addItem(new AHBottomNavigationItem( + getString(R.string.lessonplan_text), + getResources().getDrawable(R.drawable.ic_menu_timetable_24dp) + )); + bottomNavigation.addItem(new AHBottomNavigationItem( + getString(R.string.settings_text), + getResources().getDrawable(R.drawable.ic_menu_other_24dp) + )); + + bottomNavigation.setAccentColor(getResources().getColor(R.color.colorPrimary)); + bottomNavigation.setInactiveColor(Color.BLACK); + bottomNavigation.setBackgroundColor(getResources().getColor(R.color.colorBackgroundBottomNav)); + bottomNavigation.setTitleState(AHBottomNavigation.TitleState.ALWAYS_SHOW); + bottomNavigation.setOnTabSelectedListener(this); + bottomNavigation.setCurrentItem(initTabPosition); + bottomNavigation.setBehaviorTranslationEnabled(false); + } + + private void initiationViewPager() { + pagerAdapter.addFragment(new GradesFragment()); + pagerAdapter.addFragment(new AttendanceFragment()); + 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(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainContract.java new file mode 100644 index 000000000..cb1d5d7c7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainContract.java @@ -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 { + + void onTabSelected(int position, boolean wasSelected); + + void onFragmentIsReady(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainPagerAdapter.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainPagerAdapter.java new file mode 100644 index 000000000..fcc51fb54 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainPagerAdapter.java @@ -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 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(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainPresenter.java new file mode 100644 index 000000000..149e49e2f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainPresenter.java @@ -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 + 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); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/OnFragmentIsReadyListener.java b/app/src/main/java/io/github/wulkanowy/ui/main/OnFragmentIsReadyListener.java new file mode 100644 index 000000000..21e3ba12f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/OnFragmentIsReadyListener.java @@ -0,0 +1,6 @@ +package io.github.wulkanowy.ui.main; + +public interface OnFragmentIsReadyListener { + + void onFragmentIsReady(); +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/TabsData.java b/app/src/main/java/io/github/wulkanowy/ui/main/TabsData.java new file mode 100644 index 000000000..3af29534b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/TabsData.java @@ -0,0 +1,37 @@ +package io.github.wulkanowy.ui.main; + +import android.support.v4.app.Fragment; + +import java.util.ArrayList; +import java.util.List; + +public class TabsData { + + private List fragments = new ArrayList<>(); + + private List titles = new ArrayList<>(); + + public Fragment getFragment(int index) { + return fragments.get(index); + } + + public void addFragment(Fragment fragment) { + if (fragment != null) { + fragments.add(fragment); + } + } + + public int getFragmentsCount() { + return fragments.size(); + } + + public String getTitle(int index) { + return titles.get(index); + } + + public void addTitle(String title) { + if (title != null) { + titles.add(title); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java new file mode 100644 index 000000000..f8d3b3025 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java @@ -0,0 +1,36 @@ +package io.github.wulkanowy.ui.main.attendance; + +import io.github.wulkanowy.di.annotations.PerActivity; +import io.github.wulkanowy.ui.base.BaseContract; +import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; +import io.github.wulkanowy.ui.main.TabsData; + +public interface AttendanceContract { + + interface View extends BaseContract.View { + + void setActivityTitle(); + + void scrollViewPagerToPosition(int position); + + void setTabDataToAdapter(TabsData tabsData); + + void setAdapterWithTabLayout(); + + void setChildFragmentSelected(int position, boolean selected); + + boolean isMenuVisible(); + } + + @PerActivity + interface Presenter extends BaseContract.Presenter { + + void onFragmentVisible(boolean isVisible); + + void onTabSelected(int position); + + void onTabUnselected(int position); + + void onStart(View view, OnFragmentIsReadyListener listener); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java new file mode 100644 index 000000000..f5bbcb414 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java @@ -0,0 +1,91 @@ +package io.github.wulkanowy.ui.main.attendance; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import io.github.wulkanowy.R; +import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; + +public class AttendanceDialogFragment extends DialogFragment { + + private static final String ARGUMENT_KEY = "Item"; + + private AttendanceLesson lesson; + + @BindView(R.id.attendance_dialog_subject_value) + TextView subject; + + @BindView(R.id.attendance_dialog_date_value) + TextView date; + + @BindView(R.id.attendance_dialog_number_value) + TextView number; + + @BindView(R.id.attendance_dialog_description_value) + TextView description; + + public AttendanceDialogFragment() { + //empty constructor for fragment + } + + public static AttendanceDialogFragment newInstance(AttendanceLesson lesson) { + AttendanceDialogFragment dialogFragment = new AttendanceDialogFragment(); + + Bundle bundle = new Bundle(); + bundle.putSerializable(ARGUMENT_KEY, lesson); + + dialogFragment.setArguments(bundle); + + return dialogFragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + lesson = (AttendanceLesson) getArguments().getSerializable(ARGUMENT_KEY); + } + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.attendance_dialog, container, false); + + ButterKnife.bind(this, view); + + if (!lesson.getSubject().isEmpty()) { + subject.setText(lesson.getSubject()); + } + + if (!lesson.getDate().isEmpty()) { + date.setText(lesson.getDate()); + } + + if (0 != lesson.getNumber()) { + number.setText(String.valueOf(lesson.getNumber())); + } + + description.setText(lesson.getDescription()); + + if (lesson.getIsAbsenceUnexcused()) { + description.setTextColor(getResources().getColor(R.color.colorPrimaryDark)); + } + + return view; + } + + @OnClick(R.id.attendance_dialog_close) + void onClickCloseButton() { + dismiss(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java new file mode 100644 index 000000000..82bb2520b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java @@ -0,0 +1,117 @@ +package io.github.wulkanowy.ui.main.attendance; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; +import android.support.design.widget.TabLayout; +import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; +import io.github.wulkanowy.R; +import io.github.wulkanowy.di.component.FragmentComponent; +import io.github.wulkanowy.ui.base.BaseFragment; +import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; +import io.github.wulkanowy.ui.main.TabsData; + +public class AttendanceFragment extends BaseFragment implements AttendanceContract.View, TabLayout.OnTabSelectedListener { + + @BindView(R.id.attendance_fragment_viewpager) + ViewPager viewPager; + + @BindView(R.id.attendance_fragment_tab_layout) + TabLayout tabLayout; + + @Inject + AttendancePagerAdapter pagerAdapter; + + @Inject + AttendanceContract.Presenter presenter; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_attendance, container, false); + + FragmentComponent component = getFragmentComponent(); + 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 onTabSelected(TabLayout.Tab tab) { + presenter.onTabSelected(tab.getPosition()); + } + + @Override + public void onTabUnselected(TabLayout.Tab tab) { + presenter.onTabUnselected(tab.getPosition()); + } + + @Override + public void onTabReselected(TabLayout.Tab tab) { + //do nothing + } + + @Override + public void setTabDataToAdapter(TabsData tabsData) { + pagerAdapter.setTabsData(tabsData); + } + + @Override + public void setAdapterWithTabLayout() { + viewPager.setAdapter(pagerAdapter); + + tabLayout.setupWithViewPager(viewPager); + tabLayout.addOnTabSelectedListener(this); + } + + @Override + public void setChildFragmentSelected(int position, boolean selected) { + ((AttendanceTabFragment) pagerAdapter.getItem(position)).setSelected(selected); + } + + @Override + public void scrollViewPagerToPosition(int position) { + viewPager.setCurrentItem(position, false); + } + + @Override + public void setActivityTitle() { + setTitle(getString(R.string.attendance_text)); + } + + @Override + public void onError(String message) { + if (getActivity() != null) { + Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager), + message, Snackbar.LENGTH_LONG).show(); + } + } + + @Override + public void onDestroyView() { + presenter.onDestroy(); + super.onDestroyView(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java new file mode 100644 index 000000000..b103a007a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceHeaderItem.java @@ -0,0 +1,135 @@ +package io.github.wulkanowy.ui.main.attendance; + +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; + +import java.util.List; + +import butterknife.BindColor; +import butterknife.BindView; +import butterknife.ButterKnife; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem; +import eu.davidea.viewholders.ExpandableViewHolder; +import io.github.wulkanowy.R; +import io.github.wulkanowy.data.db.dao.entities.Day; + +public class AttendanceHeaderItem + extends AbstractExpandableHeaderItem { + + private Day day; + + AttendanceHeaderItem(Day day) { + this.day = day; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + AttendanceHeaderItem that = (AttendanceHeaderItem) o; + + return new EqualsBuilder() + .append(day, that.day) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(day) + .toHashCode(); + } + + @Override + public int getLayoutRes() { + return R.layout.attendance_header; + } + + @Override + public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new HeaderViewHolder(view, adapter); + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List payloads) { + holder.onBind(day, getSubItems()); + } + + static class HeaderViewHolder extends ExpandableViewHolder { + + @BindView(R.id.attendance_header_day) + TextView dayName; + + @BindView(R.id.attendance_header_date) + TextView date; + + @BindView(R.id.attendance_header_description) + TextView description; + + @BindView(R.id.attendance_header_alert_image) + ImageView alert; + + @BindView(R.id.attendance_header_free_name) + TextView freeName; + + @BindColor(R.color.secondary_text) + int secondaryColor; + + @BindColor(R.color.free_day) + int backgroundFreeDay; + + HeaderViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + view.setOnClickListener(this); + ButterKnife.bind(this, view); + } + + void onBind(Day item, List subItems) { + dayName.setText(StringUtils.capitalize(item.getDayName())); + date.setText(item.getDate()); + + int numberOfHours = countNotPresentHours(subItems); + description.setText((getContentView().getResources().getQuantityString(R.plurals.numberOfAbsences, + numberOfHours, numberOfHours))); + description.setVisibility(numberOfHours > 0 ? View.VISIBLE : View.INVISIBLE); + alert.setVisibility(isSubItemsHasChanges(subItems) ? View.VISIBLE : View.INVISIBLE); + freeName.setVisibility(subItems.isEmpty() ? View.VISIBLE : View.INVISIBLE); + + if (item.getAttendanceLessons().isEmpty()) { + ((FrameLayout) getContentView()).setForeground(null); + getContentView().setBackgroundColor(backgroundFreeDay); + dayName.setTextColor(secondaryColor); + } + } + + private int countNotPresentHours(List subItems) { + int i = 0; + for (AttendanceSubItem subItem : subItems) { + if (subItem.getLesson().getIsAbsenceUnexcused()) { + i++; + } + } + + return i; + } + + private boolean isSubItemsHasChanges(List subItems) { + for (AttendanceSubItem subItem : subItems) { + if (subItem.getLesson().getIsAbsenceUnexcused() || subItem.getLesson().getIsUnexcusedLateness()) { + return true; + } + } + + return false; + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePagerAdapter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePagerAdapter.java new file mode 100644 index 000000000..3cabff71d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePagerAdapter.java @@ -0,0 +1,38 @@ +package io.github.wulkanowy.ui.main.attendance; + +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; + +import io.github.wulkanowy.ui.main.TabsData; + +public class AttendancePagerAdapter extends FragmentStatePagerAdapter { + + private TabsData tabsData; + + public AttendancePagerAdapter(FragmentManager fragmentManager) { + super(fragmentManager); + } + + void setTabsData(TabsData tabsData) { + this.tabsData = tabsData; + } + + @Override + public Fragment getItem(int position) { + return tabsData.getFragment(position); + } + + + @Override + public int getCount() { + return tabsData.getFragmentsCount(); + } + + @Nullable + @Override + public CharSequence getPageTitle(int position) { + return tabsData.getTitle(position); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java new file mode 100644 index 000000000..73dd44ca7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java @@ -0,0 +1,111 @@ +package io.github.wulkanowy.ui.main.attendance; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import io.github.wulkanowy.data.RepositoryContract; +import io.github.wulkanowy.ui.base.BasePresenter; +import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; +import io.github.wulkanowy.ui.main.TabsData; +import io.github.wulkanowy.utils.TimeUtils; +import io.github.wulkanowy.utils.async.AbstractTask; +import io.github.wulkanowy.utils.async.AsyncListeners; + +public class AttendancePresenter extends BasePresenter + implements AttendanceContract.Presenter, AsyncListeners.OnFirstLoadingListener { + + private AbstractTask loadingTask; + + private List dates = new ArrayList<>(); + + private TabsData tabsData = new TabsData(); + + private OnFragmentIsReadyListener listener; + + private int positionToScroll; + + private boolean isFirstSight = false; + + @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(); + } + + if (dates.isEmpty()) { + dates = TimeUtils.getMondaysFromCurrentSchoolYear(); + } + positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true)); + + if (!isFirstSight) { + isFirstSight = true; + + loadingTask = new AbstractTask(); + loadingTask.setOnFirstLoadingListener(this); + loadingTask.execute(); + } + } + + @Override + public void onFragmentVisible(boolean isVisible) { + if (isVisible) { + getView().setActivityTitle(); + } + } + + @Override + public void onTabSelected(int position) { + getView().setChildFragmentSelected(position, true); + } + + @Override + public void onTabUnselected(int position) { + getView().setChildFragmentSelected(position, false); + } + + @Override + public void onDoInBackgroundLoading() throws Exception { + for (String date : dates) { + tabsData.addTitle(date); + tabsData.addFragment(AttendanceTabFragment.newInstance(date)); + } + } + + @Override + public void onCanceledLoadingAsync() { + //do nothing + + } + + @Override + public void onEndLoadingAsync(boolean result, Exception exception) { + if (result) { + getView().setTabDataToAdapter(tabsData); + getView().setAdapterWithTabLayout(); + getView().scrollViewPagerToPosition(positionToScroll); + listener.onFragmentIsReady(); + } + } + + @Override + public void onDestroy() { + isFirstSight = false; + + if (loadingTask != null) { + loadingTask.cancel(true); + loadingTask = null; + } + + super.onDestroy(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java new file mode 100644 index 000000000..6da35555a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceSubItem.java @@ -0,0 +1,119 @@ +package io.github.wulkanowy.ui.main.attendance; + +import android.content.Context; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; +import android.view.View; +import android.widget.ImageView; +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.AbstractSectionableItem; +import eu.davidea.viewholders.FlexibleViewHolder; +import io.github.wulkanowy.R; +import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; + +class AttendanceSubItem + extends AbstractSectionableItem { + + private AttendanceLesson lesson; + + AttendanceSubItem(AttendanceHeaderItem header, AttendanceLesson lesson) { + super(header); + this.lesson = lesson; + } + + AttendanceLesson getLesson() { + return lesson; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + AttendanceSubItem that = (AttendanceSubItem) o; + + return new EqualsBuilder() + .append(lesson, that.lesson) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(lesson) + .toHashCode(); + } + + @Override + public int getLayoutRes() { + return R.layout.attendance_subitem; + } + + @Override + public AttendanceSubItem.SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new AttendanceSubItem.SubItemViewHolder(view, adapter); + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, AttendanceSubItem.SubItemViewHolder holder, int position, List payloads) { + holder.onBind(lesson); + } + + static class SubItemViewHolder extends FlexibleViewHolder { + + @BindView(R.id.attendance_subItem_lesson) + TextView lessonName; + + @BindView(R.id.attendance_subItem_number) + TextView lessonNumber; + + @BindView(R.id.attendance_subItem_description) + TextView lessonDescription; + + @BindView(R.id.attendance_subItem_alert_image) + ImageView alert; + + private Context context; + + private AttendanceLesson item; + + SubItemViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); + context = view.getContext(); + view.setOnClickListener(this); + } + + void onBind(AttendanceLesson lesson) { + item = lesson; + + lessonName.setText(lesson.getSubject()); + lessonNumber.setText((String.valueOf(lesson.getNumber()))); + lessonDescription.setText(lesson.getDescription()); + alert.setVisibility(lesson.getIsAbsenceUnexcused() || lesson.getIsUnexcusedLateness() + ? View.VISIBLE : View.INVISIBLE); + } + + @Override + public void onClick(View view) { + super.onClick(view); + showDialog(); + } + + private void showDialog() { + AttendanceDialogFragment dialogFragment = AttendanceDialogFragment.newInstance(item); + dialogFragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0); + dialogFragment.show(((FragmentActivity) context).getSupportFragmentManager(), item.toString()); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java new file mode 100644 index 000000000..1bac005d6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabContract.java @@ -0,0 +1,32 @@ +package io.github.wulkanowy.ui.main.attendance; + +import java.util.List; + +import io.github.wulkanowy.ui.base.BaseContract; + +public interface AttendanceTabContract { + + interface View extends BaseContract.View { + + void updateAdapterList(List headerItems); + + void onRefreshSuccess(); + + void hideRefreshingBar(); + + void showNoItem(boolean show); + + void showProgressBar(boolean show); + } + + interface Presenter extends BaseContract.Presenter { + + void onFragmentSelected(boolean isSelected); + + void setArgumentDate(String date); + + void onStart(AttendanceTabContract.View view, boolean isPrimary); + + void onRefresh(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java new file mode 100644 index 000000000..90313daa6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabFragment.java @@ -0,0 +1,170 @@ +package io.github.wulkanowy.ui.main.attendance; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import java.util.List; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; +import io.github.wulkanowy.R; +import io.github.wulkanowy.di.component.FragmentComponent; +import io.github.wulkanowy.ui.base.BaseFragment; + +public class AttendanceTabFragment extends BaseFragment implements AttendanceTabContract.View, + SwipeRefreshLayout.OnRefreshListener { + + private static final String ARGUMENT_KEY = "date"; + + private static final String SAVED_KEY = "isSelected"; + + private boolean isPrimary = false; + + private boolean isSelected = false; + + @BindView(R.id.attendance_tab_fragment_recycler) + RecyclerView recyclerView; + + @BindView(R.id.attendance_tab_fragment_swipe_refresh) + SwipeRefreshLayout refreshLayout; + + @BindView(R.id.attendance_tab_fragment_progress_bar) + View progressBar; + + @BindView(R.id.attendance_tab_fragment_no_item_container) + View noItemView; + + @Inject + AttendanceTabContract.Presenter presenter; + + @Inject + FlexibleAdapter adapter; + + public static AttendanceTabFragment newInstance(String date) { + AttendanceTabFragment fragmentTab = new AttendanceTabFragment(); + + Bundle bundle = new Bundle(); + bundle.putString(ARGUMENT_KEY, date); + fragmentTab.setArguments(bundle); + + return fragmentTab; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (savedInstanceState != null) { + isSelected = savedInstanceState.getBoolean(SAVED_KEY, isSelected); + } + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_attendance_tab, container, false); + + FragmentComponent component = getFragmentComponent(); + if (component != null) { + component.inject(this); + setButterKnife(ButterKnife.bind(this, view)); + + if (getArguments() != null) { + presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY)); + } + + presenter.onStart(this, isPrimary); + } + return view; + } + + @Override + protected void setUpOnViewCreated(View fragmentView) { + adapter.setAutoCollapseOnExpand(true); + adapter.setAutoScrollOnExpand(true); + adapter.expandItemsAtStartUp(); + + recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext())); + recyclerView.setAdapter(adapter); + + refreshLayout.setColorSchemeResources(android.R.color.black); + refreshLayout.setOnRefreshListener(this); + + } + + @Override + public void updateAdapterList(List headerItems) { + adapter.updateDataSet(headerItems); + } + + @Override + public void setMenuVisibility(boolean menuVisible) { + super.setMenuVisibility(menuVisible); + if (presenter != null && getView() != null) { + presenter.onFragmentSelected(isSelected); + } else if (isSelected) { + isPrimary = true; + } + } + + @Override + public void onRefresh() { + presenter.onRefresh(); + } + + @Override + public void onRefreshSuccess() { + onError(R.string.timetable_refresh_success); + } + + @Override + public void hideRefreshingBar() { + refreshLayout.setRefreshing(false); + } + + @Override + public void showProgressBar(boolean show) { + progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE); + } + + @Override + public void showNoItem(boolean show) { + noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); + } + + public void setSelected(boolean selected) { + isSelected = selected; + } + + @Override + public void onError(String message) { + if (getActivity() != null) { + Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager), + message, Snackbar.LENGTH_LONG).show(); + } + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + outState.putBoolean(SAVED_KEY, isSelected); + super.onSaveInstanceState(outState); + } + + @Override + public void onDestroyView() { + isPrimary = false; + presenter.onDestroy(); + super.onDestroyView(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java new file mode 100644 index 000000000..d0d448ca8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceTabPresenter.java @@ -0,0 +1,174 @@ +package io.github.wulkanowy.ui.main.attendance; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import io.github.wulkanowy.data.RepositoryContract; +import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; +import io.github.wulkanowy.data.db.dao.entities.Day; +import io.github.wulkanowy.data.db.dao.entities.Week; +import io.github.wulkanowy.ui.base.BasePresenter; +import io.github.wulkanowy.utils.async.AbstractTask; +import io.github.wulkanowy.utils.async.AsyncListeners; + +public class AttendanceTabPresenter extends BasePresenter + implements AttendanceTabContract.Presenter, AsyncListeners.OnRefreshListener, + AsyncListeners.OnFirstLoadingListener { + + private AbstractTask refreshTask; + + private AbstractTask loadingTask; + + private List headerItems = new ArrayList<>(); + + private String date; + + private boolean isFirstSight = false; + + @Inject + AttendanceTabPresenter(RepositoryContract repository) { + super(repository); + } + + @Override + public void onStart(AttendanceTabContract.View view, boolean isPrimary) { + super.onStart(view); + getView().showProgressBar(true); + getView().showNoItem(false); + onFragmentSelected(isPrimary); + } + + @Override + public void onFragmentSelected(boolean isSelected) { + if (!isFirstSight && isSelected) { + isFirstSight = true; + + loadingTask = new AbstractTask(); + loadingTask.setOnFirstLoadingListener(this); + loadingTask.execute(); + } + } + + @Override + public void onRefresh() { + if (getView().isNetworkConnected()) { + refreshTask = new AbstractTask(); + refreshTask.setOnRefreshListener(this); + refreshTask.execute(); + } else { + getView().onNoNetworkError(); + getView().hideRefreshingBar(); + } + } + + @Override + public void onDoInBackgroundRefresh() throws Exception { + syncData(); + } + + @Override + public void onCanceledRefreshAsync() { + if (isViewAttached()) { + getView().hideRefreshingBar(); + } + } + + @Override + public void onEndRefreshAsync(boolean result, Exception exception) { + if (result) { + loadingTask = new AbstractTask(); + loadingTask.setOnFirstLoadingListener(this); + loadingTask.execute(); + + getView().onRefreshSuccess(); + } else { + getView().onError(getRepository().getErrorLoginMessage(exception)); + } + getView().hideRefreshingBar(); + } + + @Override + public void onDoInBackgroundLoading() throws Exception { + Week week = getRepository().getWeek(date); + + if (week == null || !week.getIsAttendanceSynced()) { + syncData(); + week = getRepository().getWeek(date); + } + + List dayList = week.getDayList(); + + headerItems = new ArrayList<>(); + + boolean isEmptyWeek = true; + + for (Day day : dayList) { + AttendanceHeaderItem headerItem = new AttendanceHeaderItem(day); + + if (isEmptyWeek) { + isEmptyWeek = day.getAttendanceLessons().isEmpty(); + } + + List lessonList = day.getAttendanceLessons(); + + List subItems = new ArrayList<>(); + + for (AttendanceLesson lesson : lessonList) { + lesson.setDescription(getRepository().getAttendanceLessonDescription(lesson)); + subItems.add(new AttendanceSubItem(headerItem, lesson)); + } + + headerItem.setSubItems(subItems); + headerItem.setExpanded(false); + headerItems.add(headerItem); + } + + if (isEmptyWeek) { + headerItems = new ArrayList<>(); + } + } + + @Override + public void onCanceledLoadingAsync() { + // do nothing + } + + @Override + public void onEndLoadingAsync(boolean result, Exception exception) { + if (headerItems.isEmpty()) { + getView().showNoItem(true); + getView().updateAdapterList(null); + } else { + getView().updateAdapterList(headerItems); + getView().showNoItem(false); + } + getView().showProgressBar(false); + } + + @Override + public void setArgumentDate(String date) { + this.date = date; + } + + private void syncData() throws Exception { + getRepository().syncAttendance(date); + } + + @Override + public void onDestroy() { + isFirstSight = false; + + if (refreshTask != null) { + refreshTask.cancel(true); + refreshTask = null; + } + if (loadingTask != null) { + loadingTask.cancel(true); + loadingTask = null; + } + + super.onDestroy(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardContract.java new file mode 100644 index 000000000..2488eb95c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardContract.java @@ -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 { + + void onStart(View view, OnFragmentIsReadyListener listener); + + void onFragmentVisible(boolean isVisible); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardFragment.java new file mode 100644 index 000000000..0832b5fb7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardFragment.java @@ -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)); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardPresenter.java new file mode 100644 index 000000000..58d89b399 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/dashboard/DashboardPresenter.java @@ -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 + 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(); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradeHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradeHeaderItem.java new file mode 100644 index 000000000..a66be29b9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradeHeaderItem.java @@ -0,0 +1,118 @@ +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 { + + 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 subItems) { + subjectName.setText(item.getName()); + numberText.setText(resources.getQuantityString(R.plurals.numberOfGradesPlurals, + subItems.size(), subItems.size())); + averageText.setText(getGradesAverageString(item)); + alertImage.setVisibility(isSubItemsReadAndSaveAlertView(subItems) + ? View.INVISIBLE : View.VISIBLE); + } + + private boolean isSubItemsReadAndSaveAlertView(List subItems) { + boolean isRead = true; + + for (GradesSubItem item : subItems) { + isRead = item.getGrade().getRead(); + item.setSubjectAlertImage(alertImage); + } + 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); + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesContract.java new file mode 100644 index 000000000..7deabb5e7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesContract.java @@ -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 headerItems); + + void showNoItem(boolean show); + + void onRefreshSuccessNoGrade(); + + void onRefreshSuccess(int number); + + void hideRefreshingBar(); + + void setActivityTitle(); + + boolean isMenuVisible(); + + } + + @PerActivity + interface Presenter extends BaseContract.Presenter { + + void onFragmentVisible(boolean isVisible); + + void onRefresh(); + + void onStart(View view, OnFragmentIsReadyListener listener); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesDialogFragment.java new file mode 100644 index 000000000..0d9a699f5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesDialogFragment.java @@ -0,0 +1,116 @@ +package io.github.wulkanowy.ui.main.grades; + + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import io.github.wulkanowy.R; +import io.github.wulkanowy.data.db.dao.entities.Grade; +import io.github.wulkanowy.utils.CommonUtils; + +public class GradesDialogFragment extends DialogFragment { + + private static final String ARGUMENT_KEY = "Item"; + + 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() { + //empty constructor for fragment + } + + public static GradesDialogFragment newInstance(Grade item) { + GradesDialogFragment dialogFragment = new GradesDialogFragment(); + + Bundle bundle = new Bundle(); + bundle.putSerializable(ARGUMENT_KEY, item); + + dialogFragment.setArguments(bundle); + + return dialogFragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + grade = (Grade) getArguments().getSerializable(ARGUMENT_KEY); + } + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.grade_dialog, container, false); + + ButterKnife.bind(this, view); + + subject.setText(grade.getSubject()); + value.setText(grade.getValue()); + value.setBackgroundResource(grade.getValueColor()); + weight.setText(grade.getWeight()); + date.setText(grade.getDate()); + color.setText(CommonUtils.colorHexToColorName(grade.getColor())); + teacher.setText(getTeacherString()); + description.setText(getDescriptionString()); + + + return view; + } + + @OnClick(R.id.grade_dialog_close_button) + void onClickClose() { + dismiss(); + } + + 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); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesFragment.java new file mode 100644 index 000000000..bd44f2983 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesFragment.java @@ -0,0 +1,133 @@ +package io.github.wulkanowy.ui.main.grades; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import java.util.List; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; +import io.github.wulkanowy.R; +import io.github.wulkanowy.di.component.FragmentComponent; +import io.github.wulkanowy.ui.base.BaseFragment; +import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; + +public class GradesFragment extends BaseFragment implements GradesContract.View { + + @BindView(R.id.grade_fragment_recycler) + RecyclerView recyclerView; + + @BindView(R.id.grade_fragment_no_item_container) + View noItemView; + + @BindView(R.id.grade_fragment_swipe_refresh) + SwipeRefreshLayout refreshLayout; + + @Inject + FlexibleAdapter adapter; + + @Inject + GradesContract.Presenter presenter; + + public GradesFragment() { + // empty constructor for fragment + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_grades, container, false); + + FragmentComponent component = getFragmentComponent(); + if (component != null) { + component.inject(this); + setButterKnife(ButterKnife.bind(this, view)); + presenter.onStart(this, (OnFragmentIsReadyListener) getActivity()); + } + + return view; + } + + @Override + protected void setUpOnViewCreated(View fragmentView) { + noItemView.setVisibility(View.GONE); + + adapter.setAutoCollapseOnExpand(true); + adapter.setAutoScrollOnExpand(true); + adapter.expandItemsAtStartUp(); + + recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext())); + recyclerView.setAdapter(adapter); + + refreshLayout.setColorSchemeResources(android.R.color.black); + refreshLayout.setOnRefreshListener(this); + } + + @Override + public void setMenuVisibility(boolean menuVisible) { + super.setMenuVisibility(menuVisible); + if (presenter != null) { + presenter.onFragmentVisible(menuVisible); + } + } + + @Override + public void setActivityTitle() { + setTitle(getString(R.string.grades_text)); + } + + @Override + public void onRefresh() { + presenter.onRefresh(); + } + + @Override + public void showNoItem(boolean show) { + noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); + } + + @Override + public void hideRefreshingBar() { + refreshLayout.setRefreshing(false); + } + + @Override + public void updateAdapterList(List headerItems) { + adapter.updateDataSet(headerItems); + } + + @Override + public void onRefreshSuccessNoGrade() { + onError(R.string.snackbar_no_grades); + } + + @Override + public void onRefreshSuccess(int number) { + onError(getString(R.string.snackbar_new_grade, number)); + } + + @Override + public void onError(String message) { + if (getActivity() != null) { + Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager), + message, Snackbar.LENGTH_LONG).show(); + } + } + + @Override + public void onDestroyView() { + presenter.onDestroy(); + super.onDestroyView(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesPresenter.java new file mode 100644 index 000000000..8bdad12a7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesPresenter.java @@ -0,0 +1,160 @@ +package io.github.wulkanowy.ui.main.grades; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import io.github.wulkanowy.data.RepositoryContract; +import io.github.wulkanowy.data.db.dao.entities.Grade; +import io.github.wulkanowy.data.db.dao.entities.Subject; +import io.github.wulkanowy.ui.base.BasePresenter; +import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; +import io.github.wulkanowy.utils.async.AbstractTask; +import io.github.wulkanowy.utils.async.AsyncListeners; + +public class GradesPresenter extends BasePresenter + implements GradesContract.Presenter, AsyncListeners.OnRefreshListener, + AsyncListeners.OnFirstLoadingListener { + + private AbstractTask refreshTask; + + private AbstractTask loadingTask; + + private OnFragmentIsReadyListener listener; + + private List headerItems = new ArrayList<>(); + + private boolean isFirstSight = false; + + @Inject + GradesPresenter(RepositoryContract repository) { + super(repository); + } + + @Override + public void onStart(GradesContract.View view, OnFragmentIsReadyListener listener) { + super.onStart(view); + this.listener = listener; + + if (getView().isMenuVisible()) { + getView().setActivityTitle(); + } + + if (!isFirstSight) { + isFirstSight = true; + + loadingTask = new AbstractTask(); + loadingTask.setOnFirstLoadingListener(this); + loadingTask.execute(); + } + } + + @Override + public void onFragmentVisible(boolean isVisible) { + if (isVisible) { + getView().setActivityTitle(); + } + } + + @Override + public void onRefresh() { + if (getView().isNetworkConnected()) { + refreshTask = new AbstractTask(); + refreshTask.setOnRefreshListener(this); + refreshTask.execute(); + } else { + getView().onNoNetworkError(); + getView().hideRefreshingBar(); + } + } + + @Override + public void onDoInBackgroundRefresh() throws Exception { + getRepository().syncSubjects(); + getRepository().syncGrades(); + } + + @Override + public void onCanceledRefreshAsync() { + if (isViewAttached()) { + getView().hideRefreshingBar(); + } + } + + @Override + public void onEndRefreshAsync(boolean success, Exception exception) { + if (success) { + loadingTask = new AbstractTask(); + loadingTask.setOnFirstLoadingListener(this); + loadingTask.execute(); + + int numberOfNewGrades = getRepository().getNewGrades().size(); + + if (numberOfNewGrades <= 0) { + getView().onRefreshSuccessNoGrade(); + } else { + getView().onRefreshSuccess(numberOfNewGrades); + } + } else { + getView().onError(getRepository().getErrorLoginMessage(exception)); + } + getView().hideRefreshingBar(); + } + + @Override + public void onDoInBackgroundLoading() throws Exception { + List subjectList = getRepository().getCurrentUser().getSubjectList(); + + headerItems = new ArrayList<>(); + + for (Subject subject : subjectList) { + List gradeList = subject.getGradeList(); + + if (!gradeList.isEmpty()) { + GradeHeaderItem headerItem = new GradeHeaderItem(subject); + + List subItems = new ArrayList<>(); + + for (Grade grade : gradeList) { + subItems.add(new GradesSubItem(headerItem, grade)); + } + + headerItem.setSubItems(subItems); + headerItem.setExpanded(false); + headerItems.add(headerItem); + } + } + } + + @Override + public void onCanceledLoadingAsync() { + // do nothing + } + + @Override + public void onEndLoadingAsync(boolean result, Exception exception) { + if (headerItems.isEmpty()) { + getView().showNoItem(true); + } else { + getView().updateAdapterList(headerItems); + getView().showNoItem(false); + } + listener.onFragmentIsReady(); + } + + @Override + public void onDestroy() { + isFirstSight = false; + + if (refreshTask != null) { + refreshTask.cancel(true); + refreshTask = null; + } + if (loadingTask != null) { + loadingTask.cancel(true); + loadingTask = null; + } + super.onDestroy(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSubItem.java new file mode 100644 index 000000000..102a232f4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSubItem.java @@ -0,0 +1,157 @@ +package io.github.wulkanowy.ui.main.grades; + +import android.content.Context; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; +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.AbstractSectionableItem; +import eu.davidea.viewholders.FlexibleViewHolder; +import io.github.wulkanowy.R; +import io.github.wulkanowy.data.db.dao.entities.Grade; + +public class GradesSubItem + extends AbstractSectionableItem { + + private Grade grade; + + private static int numberOfNotReadGrade; + + private View subjectAlertImage; + + GradesSubItem(GradeHeaderItem header, Grade grade) { + super(header); + this.grade = grade; + } + + public Grade getGrade() { + return grade; + } + + public void setSubjectAlertImage(View subjectAlertImage) { + this.subjectAlertImage = subjectAlertImage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + GradesSubItem that = (GradesSubItem) o; + + return new EqualsBuilder() + .append(grade, that.grade) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(grade) + .toHashCode(); + } + + @Override + public int getLayoutRes() { + return R.layout.grade_subitem; + } + + @Override + public SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new SubItemViewHolder(view, adapter); + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, SubItemViewHolder holder, int position, List payloads) { + holder.onBind(grade, subjectAlertImage); + } + + static class SubItemViewHolder extends FlexibleViewHolder { + + @BindView(R.id.grade_subitem_value) + TextView value; + + @BindView(R.id.grade_subitem_description) + TextView description; + + @BindView(R.id.grade_subitem_date) + TextView date; + + @BindView(R.id.grade_subitem_alert_image) + View alert; + + private View subjectAlertImage; + + private Context context; + + private Grade item; + + SubItemViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); + context = view.getContext(); + view.setOnClickListener(this); + } + + void onBind(Grade item, View subjectAlertImage) { + this.item = item; + this.subjectAlertImage = subjectAlertImage; + + value.setText(item.getValue()); + value.setBackgroundResource(item.getValueColor()); + date.setText(item.getDate()); + description.setText(getDescriptionString()); + alert.setVisibility(item.getRead() ? View.INVISIBLE : View.VISIBLE); + + if (!item.getRead()) { + numberOfNotReadGrade++; + } + } + + @Override + public void onClick(View view) { + super.onClick(view); + showDialog(); + + if (!item.getRead()) { + numberOfNotReadGrade--; + + if (numberOfNotReadGrade == 0) { + subjectAlertImage.setVisibility(View.INVISIBLE); + } + item.setIsNew(false); + item.setRead(true); + item.update(); + alert.setVisibility(View.INVISIBLE); + } + } + + private String getDescriptionString() { + if (item.getDescription() == null || "".equals(item.getDescription())) { + if (!"".equals(item.getSymbol())) { + return item.getSymbol(); + } else { + return context.getString(R.string.noDescription_text); + } + } else { + return item.getDescription(); + } + } + + private void showDialog() { + GradesDialogFragment dialogFragment = GradesDialogFragment.newInstance(item); + dialogFragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0); + dialogFragment.show(((FragmentActivity) context).getSupportFragmentManager(), item.toString()); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java new file mode 100644 index 000000000..eaad05056 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java @@ -0,0 +1,36 @@ +package io.github.wulkanowy.ui.main.timetable; + +import io.github.wulkanowy.di.annotations.PerActivity; +import io.github.wulkanowy.ui.base.BaseContract; +import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; +import io.github.wulkanowy.ui.main.TabsData; + +public interface TimetableContract { + + interface View extends BaseContract.View { + + void setActivityTitle(); + + void scrollViewPagerToPosition(int position); + + void setTabDataToAdapter(TabsData tabsData); + + void setAdapterWithTabLayout(); + + void setChildFragmentSelected(int position, boolean selected); + + boolean isMenuVisible(); + } + + @PerActivity + interface Presenter extends BaseContract.Presenter { + + void onFragmentVisible(boolean isVisible); + + void onTabSelected(int position); + + void onTabUnselected(int position); + + void onStart(View view, OnFragmentIsReadyListener listener); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java new file mode 100644 index 000000000..b93901953 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java @@ -0,0 +1,127 @@ +package io.github.wulkanowy.ui.main.timetable; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.apache.commons.lang3.StringUtils; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import io.github.wulkanowy.R; +import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; + +public class TimetableDialogFragment extends DialogFragment { + + private static final String ARGUMENT_KEY = "Item"; + + private TimetableLesson lesson; + + @BindView(R.id.timetable_dialog_lesson_value) + TextView lessonName; + + @BindView(R.id.timetable_dialog_teacher_value) + TextView teacher; + + @BindView(R.id.timetable_dialog_group_value) + TextView group; + + @BindView(R.id.timetable_dialog_room_value) + TextView room; + + @BindView(R.id.timetable_dialog_time_value) + TextView time; + + @BindView(R.id.timetable_dialog_description_value) + TextView description; + + @BindView(R.id.timetable_dialog_description) + View descriptionLabel; + + @BindView(R.id.timetable_dialog_teacher) + View teacherLabel; + + @BindView(R.id.timetable_dialog_group) + View groupLabel; + + public TimetableDialogFragment() { + //empty constructor for fragment + } + + public static TimetableDialogFragment newInstance(TimetableLesson lesson) { + TimetableDialogFragment dialogFragment = new TimetableDialogFragment(); + + Bundle bundle = new Bundle(); + bundle.putSerializable(ARGUMENT_KEY, lesson); + + dialogFragment.setArguments(bundle); + + return dialogFragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + lesson = (TimetableLesson) getArguments().getSerializable(ARGUMENT_KEY); + } + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.timetable_dialog, container, false); + + ButterKnife.bind(this, view); + + if (!lesson.getSubject().isEmpty()) { + lessonName.setText(lesson.getSubject()); + } + + if (!lesson.getTeacher().isEmpty()) { + teacher.setText(lesson.getTeacher()); + } else { + teacher.setVisibility(View.GONE); + teacherLabel.setVisibility(View.GONE); + } + + if (!lesson.getGroupName().isEmpty()) { + group.setText(lesson.getGroupName()); + } else { + group.setVisibility(View.GONE); + groupLabel.setVisibility(View.GONE); + } + + if (!lesson.getRoom().isEmpty()) { + room.setText(lesson.getRoom()); + } + + if (!lesson.getEndTime().isEmpty() && !lesson.getStartTime().isEmpty()) { + time.setText(getTimeString()); + } + + if (!lesson.getDescription().isEmpty()) { + description.setText(StringUtils.capitalize(lesson.getDescription())); + } else { + description.setVisibility(View.GONE); + descriptionLabel.setVisibility(View.GONE); + } + + return view; + } + + private String getTimeString() { + return String.format("%1$s - %2$s", lesson.getStartTime(), lesson.getEndTime()); + } + + @OnClick(R.id.timetable_dialog_close) + void onClickCloseButton() { + dismiss(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java new file mode 100644 index 000000000..8bef4fb22 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java @@ -0,0 +1,115 @@ +package io.github.wulkanowy.ui.main.timetable; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; +import android.support.design.widget.TabLayout; +import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; +import io.github.wulkanowy.R; +import io.github.wulkanowy.di.component.FragmentComponent; +import io.github.wulkanowy.ui.base.BaseFragment; +import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; +import io.github.wulkanowy.ui.main.TabsData; + +public class TimetableFragment extends BaseFragment implements TimetableContract.View, TabLayout.OnTabSelectedListener { + + @BindView(R.id.timetable_fragment_viewpager) + ViewPager viewPager; + + @BindView(R.id.timetable_fragment_tab_layout) + TabLayout tabLayout; + + @Inject + TimetablePagerAdapter pagerAdapter; + + @Inject + TimetableContract.Presenter presenter; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_timetable, 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 onTabSelected(TabLayout.Tab tab) { + presenter.onTabSelected(tab.getPosition()); + } + + @Override + public void onTabUnselected(TabLayout.Tab tab) { + presenter.onTabUnselected(tab.getPosition()); + } + + @Override + public void onTabReselected(TabLayout.Tab tab) { + //do nothing + } + + @Override + public void setTabDataToAdapter(TabsData tabsData) { + pagerAdapter.setTabsData(tabsData); + } + + @Override + public void setAdapterWithTabLayout() { + viewPager.setAdapter(pagerAdapter); + + tabLayout.setupWithViewPager(viewPager); + tabLayout.addOnTabSelectedListener(this); + } + + @Override + public void setChildFragmentSelected(int position, boolean selected) { + ((TimetableTabFragment) pagerAdapter.getItem(position)).setSelected(selected); + } + + @Override + public void scrollViewPagerToPosition(int position) { + viewPager.setCurrentItem(position, false); + } + + @Override + public void setActivityTitle() { + setTitle(getString(R.string.lessonplan_text)); + } + + @Override + public void onError(String message) { + if (getActivity() != null) { + Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager), + message, Snackbar.LENGTH_LONG).show(); + } + } + + @Override + public void onDestroyView() { + presenter.onDestroy(); + super.onDestroyView(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java new file mode 100644 index 000000000..39a8f0122 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java @@ -0,0 +1,119 @@ +package io.github.wulkanowy.ui.main.timetable; + +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; + +import java.util.List; + +import butterknife.BindColor; +import butterknife.BindView; +import butterknife.ButterKnife; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem; +import eu.davidea.viewholders.ExpandableViewHolder; +import io.github.wulkanowy.R; +import io.github.wulkanowy.data.db.dao.entities.Day; + +public class TimetableHeaderItem + extends AbstractExpandableHeaderItem { + + private Day day; + + TimetableHeaderItem(Day day) { + this.day = day; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + TimetableHeaderItem that = (TimetableHeaderItem) o; + + return new EqualsBuilder() + .append(day, that.day) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(day) + .toHashCode(); + } + + @Override + public int getLayoutRes() { + return R.layout.timetable_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(day, getSubItems()); + } + + static class HeaderViewHolder extends ExpandableViewHolder { + + @BindView(R.id.timetable_header_day) + TextView dayName; + + @BindView(R.id.timetable_header_date) + TextView date; + + @BindView(R.id.timetable_header_alert_image) + ImageView alert; + + @BindView(R.id.timetable_header_free_name) + TextView freeName; + + @BindColor(R.color.secondary_text) + int secondaryColor; + + @BindColor(R.color.free_day) + int backgroundFreeDay; + + HeaderViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + view.setOnClickListener(this); + ButterKnife.bind(this, view); + } + + void onBind(Day item, List subItems) { + dayName.setText(StringUtils.capitalize(item.getDayName())); + date.setText(item.getDate()); + alert.setVisibility(isSubItemNewMovedInOrChanged(subItems) ? View.VISIBLE : View.INVISIBLE); + freeName.setVisibility(item.getIsFreeDay() ? View.VISIBLE : View.INVISIBLE); + freeName.setText(item.getFreeDayName()); + + if (item.getIsFreeDay()) { + ((FrameLayout) getContentView()).setForeground(null); + getContentView().setBackgroundColor(backgroundFreeDay); + dayName.setTextColor(secondaryColor); + } + } + + private boolean isSubItemNewMovedInOrChanged(List subItems) { + boolean isAlertActive = false; + + for (TimetableSubItem subItem : subItems) { + if (subItem.getLesson().getIsMovedOrCanceled() || + subItem.getLesson().getIsNewMovedInOrChanged()) { + isAlertActive = true; + } + } + return isAlertActive; + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java new file mode 100644 index 000000000..0b8523420 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java @@ -0,0 +1,38 @@ +package io.github.wulkanowy.ui.main.timetable; + +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; + +import io.github.wulkanowy.ui.main.TabsData; + +public class TimetablePagerAdapter extends FragmentStatePagerAdapter { + + private TabsData tabsData; + + public TimetablePagerAdapter(FragmentManager fragmentManager) { + super(fragmentManager); + } + + void setTabsData(TabsData tabsData) { + this.tabsData = tabsData; + } + + @Override + public Fragment getItem(int position) { + return tabsData.getFragment(position); + } + + + @Override + public int getCount() { + return tabsData.getFragmentsCount(); + } + + @Nullable + @Override + public CharSequence getPageTitle(int position) { + return tabsData.getTitle(position); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java new file mode 100644 index 000000000..e816a1907 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java @@ -0,0 +1,110 @@ +package io.github.wulkanowy.ui.main.timetable; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import io.github.wulkanowy.data.RepositoryContract; +import io.github.wulkanowy.ui.base.BasePresenter; +import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; +import io.github.wulkanowy.ui.main.TabsData; +import io.github.wulkanowy.utils.TimeUtils; +import io.github.wulkanowy.utils.async.AbstractTask; +import io.github.wulkanowy.utils.async.AsyncListeners; + +public class TimetablePresenter extends BasePresenter + implements TimetableContract.Presenter, AsyncListeners.OnFirstLoadingListener { + + private AbstractTask loadingTask; + + private List dates = new ArrayList<>(); + + private TabsData tabsData = new TabsData(); + + private OnFragmentIsReadyListener listener; + + private int positionToScroll; + + private boolean isFirstSight = false; + + @Inject + TimetablePresenter(RepositoryContract repository) { + super(repository); + } + + @Override + public void onStart(TimetableContract.View view, OnFragmentIsReadyListener listener) { + super.onStart(view); + this.listener = listener; + + if (getView().isMenuVisible()) { + getView().setActivityTitle(); + } + + if (dates.isEmpty()) { + dates = TimeUtils.getMondaysFromCurrentSchoolYear(); + } + positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true)); + + if (!isFirstSight) { + isFirstSight = true; + + loadingTask = new AbstractTask(); + loadingTask.setOnFirstLoadingListener(this); + loadingTask.execute(); + } + } + + @Override + public void onFragmentVisible(boolean isVisible) { + if (isVisible) { + getView().setActivityTitle(); + } + } + + @Override + public void onTabSelected(int position) { + getView().setChildFragmentSelected(position, true); + } + + @Override + public void onTabUnselected(int position) { + getView().setChildFragmentSelected(position, false); + } + + @Override + public void onDoInBackgroundLoading() throws Exception { + for (String date : dates) { + tabsData.addTitle(date); + tabsData.addFragment(TimetableTabFragment.newInstance(date)); + } + } + + @Override + public void onCanceledLoadingAsync() { + //do nothing + + } + + @Override + public void onEndLoadingAsync(boolean result, Exception exception) { + if (result) { + getView().setTabDataToAdapter(tabsData); + getView().setAdapterWithTabLayout(); + getView().scrollViewPagerToPosition(positionToScroll); + listener.onFragmentIsReady(); + } + } + + @Override + public void onDestroy() { + isFirstSight = false; + + if (loadingTask != null) { + loadingTask.cancel(true); + loadingTask = null; + } + super.onDestroy(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java new file mode 100644 index 000000000..ca82f783f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java @@ -0,0 +1,141 @@ +package io.github.wulkanowy.ui.main.timetable; + +import android.content.Context; +import android.graphics.Paint; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; +import android.view.View; +import android.widget.ImageView; +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.AbstractSectionableItem; +import eu.davidea.viewholders.FlexibleViewHolder; +import io.github.wulkanowy.R; +import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; + + +public class TimetableSubItem + extends AbstractSectionableItem { + + private TimetableLesson lesson; + + public TimetableSubItem(TimetableHeaderItem header, TimetableLesson lesson) { + super(header); + this.lesson = lesson; + } + + public TimetableLesson getLesson() { + return lesson; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + TimetableSubItem that = (TimetableSubItem) o; + + return new EqualsBuilder() + .append(lesson, that.lesson) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(lesson) + .toHashCode(); + } + + @Override + public int getLayoutRes() { + return R.layout.timetable_subitem; + } + + @Override + public SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new SubItemViewHolder(view, adapter); + } + + @Override + public void bindViewHolder(FlexibleAdapter adapter, SubItemViewHolder holder, int position, List payloads) { + holder.onBind(lesson); + } + + static class SubItemViewHolder extends FlexibleViewHolder { + + @BindView(R.id.timetable_subItem_lesson) + TextView lessonName; + + @BindView(R.id.timetable_subItem_number_of_lesson) + TextView numberOfLesson; + + @BindView(R.id.timetable_subItem_time) + TextView lessonTime; + + @BindView(R.id.timetable_subItem_room) + TextView room; + + @BindView(R.id.timetable_subItem_alert_image) + ImageView alert; + + private Context context; + + private TimetableLesson item; + + SubItemViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); + context = view.getContext(); + view.setOnClickListener(this); + } + + void onBind(TimetableLesson lesson) { + item = lesson; + + lessonName.setText(lesson.getSubject()); + lessonTime.setText(getLessonTimeString()); + numberOfLesson.setText(lesson.getNumber()); + room.setText(getRoomString()); + alert.setVisibility(lesson.getIsMovedOrCanceled() || lesson.getIsNewMovedInOrChanged() + ? View.VISIBLE : View.INVISIBLE); + lessonName.setPaintFlags(lesson.getIsMovedOrCanceled() ? lessonName.getPaintFlags() + | Paint.STRIKE_THRU_TEXT_FLAG : + lessonName.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); + room.setText(getRoomString()); + } + + @Override + public void onClick(View view) { + super.onClick(view); + showDialog(); + } + + private String getLessonTimeString() { + return String.format("%1$s - %2$s", item.getStartTime(), item.getEndTime()); + } + + private String getRoomString() { + if (!item.getRoom().isEmpty()) { + return context.getString(R.string.timetable_subitem_room, item.getRoom()); + } else { + return item.getRoom(); + } + } + + private void showDialog() { + TimetableDialogFragment dialogFragment = TimetableDialogFragment.newInstance(item); + dialogFragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0); + dialogFragment.show(((FragmentActivity) context).getSupportFragmentManager(), item.toString()); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabContract.java new file mode 100644 index 000000000..d17e6e557 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabContract.java @@ -0,0 +1,34 @@ +package io.github.wulkanowy.ui.main.timetable; + +import java.util.List; + +import io.github.wulkanowy.ui.base.BaseContract; + +public interface TimetableTabContract { + + interface View extends BaseContract.View { + + void updateAdapterList(List headerItems); + + void onRefreshSuccess(); + + void hideRefreshingBar(); + + void showNoItem(boolean show); + + void showProgressBar(boolean show); + + void setFreeWeekName(String text); + } + + interface Presenter extends BaseContract.Presenter { + + void onFragmentSelected(boolean isSelected); + + void setArgumentDate(String date); + + void onStart(View view, boolean isPrimary); + + void onRefresh(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabFragment.java new file mode 100644 index 000000000..65841247d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabFragment.java @@ -0,0 +1,178 @@ +package io.github.wulkanowy.ui.main.timetable; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import java.util.List; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; +import io.github.wulkanowy.R; +import io.github.wulkanowy.di.component.FragmentComponent; +import io.github.wulkanowy.ui.base.BaseFragment; + +public class TimetableTabFragment extends BaseFragment implements TimetableTabContract.View, + SwipeRefreshLayout.OnRefreshListener { + + private static final String ARGUMENT_KEY = "date"; + + private static final String SAVED_KEY = "isSelected"; + + private boolean isPrimary = false; + + private boolean isSelected = false; + + @BindView(R.id.timetable_tab_fragment_recycler) + RecyclerView recyclerView; + + @BindView(R.id.timetable_tab_fragment_swipe_refresh) + SwipeRefreshLayout refreshLayout; + + @BindView(R.id.timetable_tab_fragment_progress_bar) + View progressBar; + + @BindView(R.id.timetable_tab_fragment_no_item_container) + View noItemView; + + @BindView(R.id.timetable_tab_fragment_no_item_name) + TextView noItemName; + + @Inject + TimetableTabContract.Presenter presenter; + + @Inject + FlexibleAdapter adapter; + + public static TimetableTabFragment newInstance(String date) { + TimetableTabFragment fragmentTab = new TimetableTabFragment(); + + Bundle bundle = new Bundle(); + bundle.putString(ARGUMENT_KEY, date); + fragmentTab.setArguments(bundle); + + return fragmentTab; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + isSelected = savedInstanceState.getBoolean(SAVED_KEY, isSelected); + } + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_timetable_tab, container, false); + + FragmentComponent component = getFragmentComponent(); + if (component != null) { + component.inject(this); + setButterKnife(ButterKnife.bind(this, view)); + + if (getArguments() != null) { + presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY)); + } + + presenter.onStart(this, isPrimary); + } + return view; + } + + @Override + protected void setUpOnViewCreated(View fragmentView) { + adapter.setAutoCollapseOnExpand(true); + adapter.setAutoScrollOnExpand(true); + adapter.expandItemsAtStartUp(); + + recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(fragmentView.getContext())); + recyclerView.setAdapter(adapter); + + refreshLayout.setColorSchemeResources(android.R.color.black); + refreshLayout.setOnRefreshListener(this); + + } + + @Override + public void updateAdapterList(List headerItems) { + adapter.updateDataSet(headerItems); + } + + @Override + public void setMenuVisibility(boolean menuVisible) { + super.setMenuVisibility(menuVisible); + if (presenter != null && getView() != null) { + presenter.onFragmentSelected(isSelected); + } else if (isSelected) { + isPrimary = true; + } + } + + @Override + public void setFreeWeekName(String text) { + noItemName.setText(text); + } + + @Override + public void onRefresh() { + presenter.onRefresh(); + } + + @Override + public void onRefreshSuccess() { + onError(R.string.timetable_refresh_success); + } + + @Override + public void hideRefreshingBar() { + refreshLayout.setRefreshing(false); + } + + @Override + public void showProgressBar(boolean show) { + progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE); + } + + @Override + public void showNoItem(boolean show) { + noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); + } + + public void setSelected(boolean selected) { + isSelected = selected; + } + + @Override + public void onError(String message) { + if (getActivity() != null) { + Snackbar.make(getActivity().findViewById(R.id.main_activity_view_pager), + message, Snackbar.LENGTH_LONG).show(); + } + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + outState.putBoolean(SAVED_KEY, isSelected); + super.onSaveInstanceState(outState); + } + + @Override + public void onDestroyView() { + isPrimary = false; + presenter.onDestroy(); + super.onDestroyView(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java new file mode 100644 index 000000000..e5eb499f2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java @@ -0,0 +1,177 @@ +package io.github.wulkanowy.ui.main.timetable; + + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import io.github.wulkanowy.data.RepositoryContract; +import io.github.wulkanowy.data.db.dao.entities.Day; +import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; +import io.github.wulkanowy.data.db.dao.entities.Week; +import io.github.wulkanowy.ui.base.BasePresenter; +import io.github.wulkanowy.utils.async.AbstractTask; +import io.github.wulkanowy.utils.async.AsyncListeners; + +public class TimetableTabPresenter extends BasePresenter + implements TimetableTabContract.Presenter, AsyncListeners.OnRefreshListener, + AsyncListeners.OnFirstLoadingListener { + + private AbstractTask refreshTask; + + private AbstractTask loadingTask; + + private List headerItems = new ArrayList<>(); + + private String date; + + private String freeWeekName; + + private boolean isFirstSight = false; + + @Inject + TimetableTabPresenter(RepositoryContract repository) { + super(repository); + } + + @Override + public void onStart(TimetableTabContract.View view, boolean isPrimary) { + super.onStart(view); + getView().showProgressBar(true); + getView().showNoItem(false); + onFragmentSelected(isPrimary); + } + + @Override + public void onFragmentSelected(boolean isSelected) { + if (!isFirstSight && isSelected) { + isFirstSight = true; + + loadingTask = new AbstractTask(); + loadingTask.setOnFirstLoadingListener(this); + loadingTask.execute(); + } + } + + @Override + public void onRefresh() { + if (getView().isNetworkConnected()) { + refreshTask = new AbstractTask(); + refreshTask.setOnRefreshListener(this); + refreshTask.execute(); + } else { + getView().onNoNetworkError(); + getView().hideRefreshingBar(); + } + } + + @Override + public void onDoInBackgroundRefresh() throws Exception { + syncData(); + } + + @Override + public void onCanceledRefreshAsync() { + if (isViewAttached()) { + getView().hideRefreshingBar(); + } + } + + @Override + public void onEndRefreshAsync(boolean result, Exception exception) { + if (result) { + loadingTask = new AbstractTask(); + loadingTask.setOnFirstLoadingListener(this); + loadingTask.execute(); + + getView().onRefreshSuccess(); + } else { + getView().onError(getRepository().getErrorLoginMessage(exception)); + } + getView().hideRefreshingBar(); + } + + @Override + public void onDoInBackgroundLoading() throws Exception { + Week week = getRepository().getWeek(date); + + if (week == null || !week.getIsTimetableSynced()) { + syncData(); + week = getRepository().getWeek(date); + } + + List dayList = week.getDayList(); + + headerItems = new ArrayList<>(); + + boolean isFreeWeek = true; + + for (Day day : dayList) { + TimetableHeaderItem headerItem = new TimetableHeaderItem(day); + + if (isFreeWeek) { + isFreeWeek = day.getIsFreeDay(); + } + + List lessonList = day.getTimetableLessons(); + + List subItems = new ArrayList<>(); + + for (TimetableLesson lesson : lessonList) { + subItems.add(new TimetableSubItem(headerItem, lesson)); + } + + headerItem.setSubItems(subItems); + headerItem.setExpanded(false); + headerItems.add(headerItem); + } + + if (isFreeWeek) { + freeWeekName = dayList.get(4).getFreeDayName(); + headerItems = new ArrayList<>(); + } + } + + @Override + public void onCanceledLoadingAsync() { + // do nothing + } + + @Override + public void onEndLoadingAsync(boolean result, Exception exception) { + if (headerItems.isEmpty()) { + getView().showNoItem(true); + getView().setFreeWeekName(freeWeekName); + getView().updateAdapterList(null); + } else { + getView().updateAdapterList(headerItems); + getView().showNoItem(false); + } + getView().showProgressBar(false); + } + + @Override + public void setArgumentDate(String date) { + this.date = date; + } + + private void syncData() throws Exception { + getRepository().syncTimetable(date); + } + + @Override + public void onDestroy() { + isFirstSight = false; + + if (refreshTask != null) { + refreshTask.cancel(true); + refreshTask = null; + } + if (loadingTask != null) { + loadingTask.cancel(true); + loadingTask = null; + } + super.onDestroy(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt deleted file mode 100644 index 52caf731e..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt +++ /dev/null @@ -1,93 +0,0 @@ -package io.github.wulkanowy.ui.modules.about - -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.mikepenz.aboutlibraries.LibsBuilder -import com.mikepenz.aboutlibraries.LibsFragmentCompat -import io.github.wulkanowy.BuildConfig -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.withOnExtraListener -import javax.inject.Inject - -class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { - - @Inject - lateinit var presenter: AboutPresenter - - @Inject - lateinit var fragmentCompat: LibsFragmentCompat - - companion object { - fun newInstance() = AboutFragment() - } - - override val titleStringId: Int - get() = R.string.about_title - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - presenter.onAttachView(this) - return Bundle().apply { - putSerializable("data", LibsBuilder() - .withAboutAppName(getString(R.string.app_name)) - .withAboutVersionShown(true) - .withAboutIconShown(true) - .withLicenseShown(true) - .withAboutSpecial1(getString(R.string.about_discord_invite)) - .withAboutSpecial2(getString(R.string.about_homepage)) - .withAboutSpecial3(getString(R.string.about_feedback)) - .withFields(R.string::class.java.fields) - .withCheckCachedDetection(false) - .withExcludedLibraries("fastadapter", "AndroidIconics", "Jsoup", "Retrofit", "okio", - "OkHttp", "Butterknife", "CircleImageView") - .withOnExtraListener { presenter.onExtraSelect(it) }) - }.let { - fragmentCompat.onCreateView(inflater.context, inflater, container, savedInstanceState, it) - } - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - fragmentCompat.onViewCreated(view, savedInstanceState) - } - - override fun openDiscordInviteView() { - startActivity(Intent.parseUri("https://discord.gg/vccAQBr", 0)) - } - - override fun openHomepageWebView() { - startActivity(Intent.parseUri("https://wulkanowy.github.io/", 0)) - } - - override fun openEmailClientView() { - val intent = Intent(Intent.ACTION_SENDTO).apply { - data = Uri.parse("mailto:") - putExtra(Intent.EXTRA_EMAIL, Array(1) { "wulkanowyinc@gmail.com" }) - putExtra(Intent.EXTRA_SUBJECT, "Zgłoszenie błędu") - putExtra(Intent.EXTRA_TEXT, "Tu umieść treść zgłoszenia\n\n" + "-".repeat(40) + "\n" + """ - Build: ${BuildConfig.VERSION_CODE} - SDK: ${android.os.Build.VERSION.SDK_INT} - Device: ${android.os.Build.MANUFACTURER} ${android.os.Build.MODEL} - """.trimIndent()) - } - - context?.let { - if (intent.resolveActivity(it.packageManager) != null) { - startActivity(Intent.createChooser(intent, getString(R.string.about_feedback))) - } else { - startActivity(Intent.parseUri("https://github.com/wulkanowy/wulkanowy/issues", 0)) - } - } - } - - override fun onDestroyView() { - fragmentCompat.onDestroyView() - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutModule.kt deleted file mode 100644 index cc5ba7cf3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutModule.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.wulkanowy.ui.modules.about - -import com.mikepenz.aboutlibraries.LibsFragmentCompat -import dagger.Module -import dagger.Provides -import io.github.wulkanowy.di.scopes.PerFragment - -@Module -class AboutModule { - - @PerFragment - @Provides - fun provideLibsFragmentCompat() = LibsFragmentCompat() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt deleted file mode 100644 index 116dbef35..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.wulkanowy.ui.modules.about - -import com.mikepenz.aboutlibraries.Libs -import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL1 -import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL2 -import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL3 -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import timber.log.Timber -import javax.inject.Inject - -class AboutPresenter @Inject constructor( - errorHandler: ErrorHandler, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: AboutView) { - super.onAttachView(view) - Timber.i("About view is attached") - } - - fun onExtraSelect(type: Libs.SpecialButton?) { - view?.run { - when (type) { - SPECIAL1 -> { - Timber.i("Opening discord invide page") - analytics.logEvent("open_page", "name" to "discord") - openDiscordInviteView() - } - SPECIAL2 -> { - Timber.i("Opening home page") - analytics.logEvent("open_page", "name" to "home") - openHomepageWebView() - } - SPECIAL3 -> { - Timber.i("Opening email client") - analytics.logEvent("open_page", "name" to "email") - openEmailClientView() - } - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt deleted file mode 100644 index 5b206ad82..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.wulkanowy.ui.modules.about - -import io.github.wulkanowy.ui.base.BaseView - -interface AboutView : BaseView { - - fun openDiscordInviteView() - - fun openEmailClientView() - - fun openHomepageWebView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt deleted file mode 100644 index 32458f276..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt +++ /dev/null @@ -1,112 +0,0 @@ -package io.github.wulkanowy.ui.modules.account - -import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK -import android.content.Intent.FLAG_ACTIVITY_NEW_TASK -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import android.widget.Toast.LENGTH_LONG -import androidx.appcompat.app.AlertDialog -import dagger.android.support.DaggerAppCompatDialogFragment -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.dialog_account.* -import javax.inject.Inject - -class AccountDialog : DaggerAppCompatDialogFragment(), AccountView { - - @Inject - lateinit var presenter: AccountPresenter - - @Inject - lateinit var accountAdapter: FlexibleAdapter> - - companion object { - fun newInstance() = AccountDialog() - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_account, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this) - } - - override fun initView() { - accountAdapter.setOnItemClickListener { presenter.onItemSelected(it) } - - accountDialogAdd.setOnClickListener { presenter.onAddSelected() } - accountDialogRemove.setOnClickListener { presenter.onRemoveSelected() } - accountDialogRecycler.apply { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = accountAdapter - } - } - - override fun updateData(data: List) { - accountAdapter.updateDataSet(data) - } - - override fun showError(text: String, error: Throwable) { - showMessage(text) - } - - override fun showMessage(text: String) { - Toast.makeText(context, text, LENGTH_LONG).show() - } - - override fun dismissView() { - dismiss() - } - - override fun openLoginView() { - activity?.also { - startActivity(LoginActivity.getStartIntent(it)) - } - } - - override fun openClearLoginView() { - activity?.also { - startActivity(LoginActivity.getStartIntent(it) - .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) }) - } - } - - override fun showConfirmDialog() { - context?.let { - AlertDialog.Builder(it) - .setTitle(R.string.account_logout_student) - .setMessage(R.string.account_confirm) - .setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() } - .setNegativeButton(android.R.string.cancel) { _, _ -> } - .show() - } - } - - override fun recreateView() { - activity?.also { - startActivity(MainActivity.getStartIntent(it)) - it.finish() - } - } - - override fun onDestroy() { - presenter.onDetachView() - super.onDestroy() - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt deleted file mode 100644 index cb7aabda7..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.modules.account - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Student -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_account.* - -class AccountItem(val student: Student) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_account - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - accountItemName.text = student.studentName - accountItemSchool.text = student.schoolName - accountItemImage.setBackgroundResource(if (student.isCurrent) R.drawable.ic_account_circular_border else 0) - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as AccountItem - - if (student != other.student) return false - - return true - } - - override fun hashCode(): Int { - var result = student.hashCode() - result = 31 * result + student.id.toInt() - return result - } - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt deleted file mode 100644 index 0fc6cc45c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt +++ /dev/null @@ -1,102 +0,0 @@ -package io.github.wulkanowy.ui.modules.account - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.services.sync.SyncManager -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.SchedulersProvider -import io.reactivex.Single -import timber.log.Timber -import javax.inject.Inject - -class AccountPresenter @Inject constructor( - private val errorHandler: ErrorHandler, - private val studentRepository: StudentRepository, - private val syncManager: SyncManager, - private val schedulers: SchedulersProvider -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: AccountView) { - super.onAttachView(view) - Timber.i("Account dialog is attached") - view.initView() - loadData() - } - - fun onAddSelected() { - Timber.i("Select add account") - view?.openLoginView() - } - - fun onRemoveSelected() { - Timber.i("Select remove account") - view?.showConfirmDialog() - } - - fun onLogoutConfirm() { - Timber.i("Attempt to logout current user ") - disposable.add(studentRepository.getCurrentStudent() - .flatMapCompletable { studentRepository.logoutStudent(it) } - .andThen(studentRepository.getSavedStudents(false)) - .flatMap { - if (it.isNotEmpty()) studentRepository.switchStudent(it[0]).toSingle { it } - else Single.just(it) - } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { view?.dismissView() } - .subscribe({ - view?.apply { - if (it.isEmpty()) { - Timber.i("Logout result: Open login view") - syncManager.stopSyncWorker() - openClearLoginView() - } else { - Timber.i("Logout result: Switch to another student") - recreateView() - } - } - }, { - Timber.i("Logout result: An exception occurred") - errorHandler.dispatch(it) - })) - } - - fun onItemSelected(item: AbstractFlexibleItem<*>) { - if (item is AccountItem) { - Timber.i("Select student item ${item.student.id}") - if (item.student.isCurrent) { - view?.dismissView() - } else { - Timber.i("Attempt to change a student") - disposable.add(studentRepository.switchStudent(item.student) - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ - Timber.i("Change a student result: Success") - view?.recreateView() - }, { - Timber.i("Change a student result: An exception occurred") - errorHandler.dispatch(it) - })) - } - } - } - - private fun loadData() { - Timber.i("Loading account data started") - disposable.add(studentRepository.getSavedStudents(false) - .map { it.map { item -> AccountItem(item) } } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ - Timber.i("Loading account result: Success") - view?.updateData(it) - }, { - Timber.i("Loading account result: An exception occurred") - errorHandler.dispatch(it) - })) - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt deleted file mode 100644 index ca74998a1..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.ui.modules.account - -import io.github.wulkanowy.ui.base.BaseView - -interface AccountView : BaseView { - - fun initView() - - fun updateData(data: List) - - fun dismissView() - - fun showConfirmDialog() - - fun openLoginView() - - fun openClearLoginView() - - fun recreateView() -} - diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt deleted file mode 100644 index 611dd999e..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_attendance.* - -class AttendanceDialog : DialogFragment() { - - private lateinit var attendance: Attendance - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(exam: Attendance): AttendanceDialog { - return AttendanceDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - attendance = getSerializable(ARGUMENT_KEY) as Attendance - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_attendance, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - attendanceDialogSubject.text = attendance.subject - attendanceDialogDescription.text = attendance.name - attendanceDialogDate.text = attendance.date.toFormattedString() - attendanceDialogNumber.text = attendance.number.toString() - attendanceDialogClose.setOnClickListener { dismiss() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt deleted file mode 100644 index f0cab7f11..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt +++ /dev/null @@ -1,158 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_attendance.* -import javax.inject.Inject - -class AttendanceFragment : BaseSessionFragment(), AttendanceView, MainView.MainChildView, MainView.TitledView { - - @Inject - lateinit var presenter: AttendancePresenter - - @Inject - lateinit var attendanceAdapter: FlexibleAdapter> - - companion object { - private const val SAVED_DATE_KEY = "CURRENT_DATE" - - fun newInstance() = AttendanceFragment() - } - - override val titleStringId: Int - get() = R.string.attendance_title - - override val isViewEmpty: Boolean - get() = attendanceAdapter.isEmpty - - override val currentStackSize: Int? - get() = (activity as? MainActivity)?.currentStackSize - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_attendance, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = attendanceRecycler - presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) - } - - override fun initView() { - attendanceAdapter.apply { - setOnItemClickListener { presenter.onAttendanceItemSelected(it) } - } - - attendanceRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = attendanceAdapter - addItemDecoration(FlexibleItemDecoration(context) - .withDefaultDivider() - .withDrawDividerOnLastItem(false) - ) - } - attendanceSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - attendancePreviousButton.setOnClickListener { presenter.onPreviousDay() } - attendanceNextButton.setOnClickListener { presenter.onNextDay() } - } - - override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { - inflater?.inflate(R.menu.action_menu_attendance, menu) - } - - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - return if (item?.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected() - else false - } - - override fun updateData(data: List) { - attendanceAdapter.updateDataSet(data, true) - } - - override fun updateNavigationDay(date: String) { - attendanceNavDate.text = date - } - - override fun clearData() { - attendanceAdapter.clear() - } - - override fun resetView() { - attendanceRecycler.smoothScrollToPosition(0) - } - - override fun onFragmentReselected() { - presenter.onViewReselected() - } - - override fun popView() { - (activity as? MainActivity)?.popView() - } - - override fun showEmpty(show: Boolean) { - attendanceEmpty.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showProgress(show: Boolean) { - attendanceProgress.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun enableSwipe(enable: Boolean) { - attendanceSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - attendanceRecycler.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun hideRefresh() { - attendanceSwipe.isRefreshing = false - } - - override fun showPreButton(show: Boolean) { - attendancePreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showNextButton(show: Boolean) { - attendanceNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showAttendanceDialog(lesson: Attendance) { - (activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson)) - } - - override fun openSummaryView() { - (activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance()) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt deleted file mode 100644 index 16a140cbf..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance - -import android.view.View -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Attendance -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_attendance.* - -class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_attendance - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - attendanceItemNumber.text = attendance.number.toString() - attendanceItemSubject.text = attendance.subject - attendanceItemDescription.text = attendance.name - attendanceItemAlert.visibility = attendance.run { if (absence && !excused) VISIBLE else INVISIBLE } - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as AttendanceItem - - if (attendance != other.attendance) return false - - return true - } - - override fun hashCode(): Int { - var result = attendance.hashCode() - result = 31 * result + attendance.id.toInt() - return result - } - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt deleted file mode 100644 index ec471c16a..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ /dev/null @@ -1,141 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance - -import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.isHolidays -import io.github.wulkanowy.utils.nextSchoolDay -import io.github.wulkanowy.utils.previousOrSameSchoolDay -import io.github.wulkanowy.utils.previousSchoolDay -import io.github.wulkanowy.utils.toFormattedString -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDate.now -import org.threeten.bp.LocalDate.ofEpochDay -import timber.log.Timber -import java.util.concurrent.TimeUnit.MILLISECONDS -import javax.inject.Inject - -class AttendancePresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val attendanceRepository: AttendanceRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val prefRepository: PreferencesRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - lateinit var currentDate: LocalDate - private set - - fun onAttachView(view: AttendanceView, date: Long?) { - super.onAttachView(view) - Timber.i("Attendance view is attached") - view.initView() - loadData(ofEpochDay(date ?: now().previousOrSameSchoolDay.toEpochDay())) - reloadView() - } - - fun onPreviousDay() { - loadData(currentDate.previousSchoolDay) - reloadView() - } - - fun onNextDay() { - loadData(currentDate.nextSchoolDay) - reloadView() - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the attendance") - loadData(currentDate, true) - } - - fun onViewReselected() { - Timber.i("Attendance view is reselected") - view?.also { view -> - if (view.currentStackSize == 1) { - now().previousOrSameSchoolDay.also { - if (currentDate != it) { - loadData(it) - reloadView() - } else if (!view.isViewEmpty) view.resetView() - } - } else view.popView() - } - } - - fun onAttendanceItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is AttendanceItem) { - Timber.i("Select attendance item ${item.attendance.id}") - view?.showAttendanceDialog(item.attendance) - } - } - - fun onSummarySwitchSelected(): Boolean { - view?.openSummaryView() - return true - } - - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - Timber.i("Loading attendance data started") - currentDate = date - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .delay(200, MILLISECONDS) - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { attendanceRepository.getAttendance(it, date, date, forceRefresh) } - .map { list -> - if (prefRepository.isShowPresent) list - else list.filter { !it.presence } - } - .map { items -> items.map { AttendanceItem(it) } } - .map { items -> items.sortedBy { it.attendance.number } } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - } - } - .subscribe({ - Timber.i("Loading attendance result: Success") - view?.apply { - updateData(it) - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - } - analytics.logEvent("load_attendance", "items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd")) - }) { - Timber.i("Loading attendance result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - } - ) - } - } - - private fun reloadView() { - Timber.i("Reload attendance view with the date ${currentDate.toFormattedString()}") - view?.apply { - showProgress(true) - enableSwipe(false) - showContent(false) - showEmpty(false) - clearData() - showNextButton(!currentDate.plusDays(1).isHolidays) - showPreButton(!currentDate.minusDays(1).isHolidays) - updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize()) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt deleted file mode 100644 index ef3b874b6..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance - -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface AttendanceView : BaseSessionView { - - val isViewEmpty: Boolean - - val currentStackSize: Int? - - fun initView() - - fun updateData(data: List) - - fun updateNavigationDay(date: String) - - fun clearData() - - fun hideRefresh() - - fun resetView() - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun showPreButton(show: Boolean) - - fun showNextButton(show: Boolean) - - fun showAttendanceDialog(lesson: Attendance) - - fun openSummaryView() - - fun popView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt deleted file mode 100644 index b8d2c9528..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt +++ /dev/null @@ -1,125 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.summary - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import android.widget.ArrayAdapter -import android.widget.TextView -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnItemSelectedListener -import kotlinx.android.synthetic.main.fragment_attendance_summary.* -import javax.inject.Inject - -class AttendanceSummaryFragment : BaseSessionFragment(), AttendanceSummaryView, MainView.TitledView { - - @Inject - lateinit var presenter: AttendanceSummaryPresenter - - @Inject - lateinit var attendanceSummaryAdapter: FlexibleAdapter> - - private lateinit var subjectsAdapter: ArrayAdapter - - companion object { - private const val SAVED_SUBJECT_KEY = "CURRENT_SUBJECT" - - fun newInstance() = AttendanceSummaryFragment() - } - - override val titleStringId: Int - get() = R.string.attendance_title - - override val isViewEmpty - get() = attendanceSummaryAdapter.isEmpty - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_attendance_summary, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = attendanceSummaryRecycler - presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SUBJECT_KEY)) - } - - override fun initView() { - attendanceSummaryRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = attendanceSummaryAdapter - } - attendanceSummarySwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - - context?.let { - subjectsAdapter = ArrayAdapter(it, android.R.layout.simple_spinner_item, ArrayList()) - subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject) - } - - attendanceSummarySubjects.run { - adapter = subjectsAdapter - setOnItemSelectedListener { presenter.onSubjectSelected((it as TextView).text.toString()) } - } - } - - override fun updateSubjects(data: ArrayList) { - subjectsAdapter.run { - clear() - addAll(data) - notifyDataSetChanged() - } - } - - override fun updateDataSet(data: List, header: AttendanceSummaryScrollableHeader) { - attendanceSummaryAdapter.apply { - updateDataSet(data, true) - removeAllScrollableHeaders() - addScrollableHeader(header) - } - } - - override fun clearView() { - attendanceSummaryAdapter.clear() - } - - override fun showEmpty(show: Boolean) { - attendanceSummaryEmpty.visibility = if (show) VISIBLE else GONE - } - - override fun showProgress(show: Boolean) { - attendanceSummaryProgress.visibility = if (show) VISIBLE else GONE - } - - override fun enableSwipe(enable: Boolean) { - attendanceSummarySwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - attendanceSummaryRecycler.visibility = if (show) VISIBLE else GONE - } - - override fun showSubjects(show: Boolean) { - attendanceSummarySubjectsContainer.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun hideRefresh() { - attendanceSummarySwipe.isRefreshing = false - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putInt(SAVED_SUBJECT_KEY, presenter.currentSubjectId) - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt deleted file mode 100644 index 265d6ce44..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt +++ /dev/null @@ -1,80 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.summary - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_attendance_summary.* - -class AttendanceSummaryItem( - private val month: String, - private val percentage: String, - private val present: String, - private val absence: String, - private val excusedAbsence: String, - private val schoolAbsence: String, - private val exemption: String, - private val lateness: String, - private val excusedLateness: String -) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_attendance_summary - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - attendanceSummaryMonth.text = month - attendanceSummaryPercentage.text = percentage - attendanceSummaryPresent.text = present - attendanceSummaryAbsenceUnexcused.text = absence - attendanceSummaryAbsenceExcused.text = excusedAbsence - attendanceSummaryAbsenceSchool.text = schoolAbsence - attendanceSummaryExemption.text = exemption - attendanceSummaryLatenessUnexcused.text = lateness - attendanceSummaryLatenessExcused.text = excusedLateness - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as AttendanceSummaryItem - - if (month != other.month) return false - if (percentage != other.percentage) return false - if (present != other.present) return false - if (absence != other.absence) return false - if (excusedAbsence != other.excusedAbsence) return false - if (schoolAbsence != other.schoolAbsence) return false - if (exemption != other.exemption) return false - if (lateness != other.lateness) return false - if (excusedLateness != other.excusedLateness) return false - - return true - } - - override fun hashCode(): Int { - var result = month.hashCode() - result = 31 * result + percentage.hashCode() - result = 31 * result + present.hashCode() - result = 31 * result + absence.hashCode() - result = 31 * result + excusedAbsence.hashCode() - result = 31 * result + schoolAbsence.hashCode() - result = 31 * result + exemption.hashCode() - result = 31 * result + lateness.hashCode() - result = 31 * result + excusedLateness.hashCode() - return result - } - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt deleted file mode 100644 index 8b17f7437..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt +++ /dev/null @@ -1,140 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.summary - -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.github.wulkanowy.data.db.entities.Subject -import io.github.wulkanowy.data.repositories.attendancesummary.AttendanceSummaryRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.subject.SubjectRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.calculatePercentage -import io.github.wulkanowy.utils.getFormattedName -import timber.log.Timber -import java.lang.String.format -import java.util.Locale.FRANCE -import java.util.concurrent.TimeUnit.MILLISECONDS -import javax.inject.Inject - -class AttendanceSummaryPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val attendanceSummaryRepository: AttendanceSummaryRepository, - private val subjectRepository: SubjectRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val schedulers: SchedulersProvider, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - private var subjects = emptyList() - - var currentSubjectId = -1 - private set - - fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) { - super.onAttachView(view) - Timber.i("Attendance summary view is attached with subject id ${subjectId ?: -1}") - view.initView() - loadData(subjectId ?: -1) - loadSubjects() - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the attendance summary") - loadData(currentSubjectId, true) - } - - fun onSubjectSelected(name: String) { - Timber.i("Select attendance summary subject $name") - view?.run { - showContent(false) - showProgress(true) - enableSwipe(false) - clearView() - } - (subjects.singleOrNull { it.name == name }?.realId ?: -1).let { - if (it != currentSubjectId) loadData(it) - } - } - - private fun loadData(subjectId: Int, forceRefresh: Boolean = false) { - Timber.i("Loading attendance summary data started") - currentSubjectId = subjectId - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .delay(200, MILLISECONDS) - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { attendanceSummaryRepository.getAttendanceSummary(it, subjectId, forceRefresh) } - .map { createAttendanceSummaryItems(it) to AttendanceSummaryScrollableHeader(formatPercentage(it.calculatePercentage())) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - } - } - .subscribe({ - Timber.i("Loading attendance summary result: Success") - view?.apply { - showEmpty(it.first.isEmpty()) - showContent(it.first.isNotEmpty()) - updateDataSet(it.first, it.second) - } - analytics.logEvent("load_attendance_summary", "items" to it.first.size, "force_refresh" to forceRefresh, "item_id" to subjectId) - }) { - Timber.i("Loading attendance summary result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - } - ) - } - } - - private fun loadSubjects() { - Timber.i("Loading attendance summary subjects started") - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { subjectRepository.getSubjects(it) } - .doOnSuccess { subjects = it } - .map { ArrayList(it.map { subject -> subject.name }) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ - Timber.i("Loading attendance summary subjects result: Success") - view?.run { - view?.updateSubjects(it) - showSubjects(true) - } - }, { - Timber.i("Loading attendance summary subjects result: An exception occurred") - errorHandler.dispatch(it) - }) - ) - } - - private fun createAttendanceSummaryItems(attendanceSummary: List): List { - return attendanceSummary.sortedByDescending { it.id }.map { - AttendanceSummaryItem( - month = it.month.getFormattedName(), - percentage = formatPercentage(it.calculatePercentage()), - present = it.presence.toString(), - absence = it.absence.toString(), - excusedAbsence = it.absenceExcused.toString(), - schoolAbsence = it.absenceForSchoolReasons.toString(), - exemption = it.exemption.toString(), - lateness = it.lateness.toString(), - excusedLateness = it.latenessExcused.toString() - ) - } - } - - private fun formatPercentage(percentage: Double): String { - return if (percentage == 0.0) "0%" - else "${format(FRANCE, "%.2f", percentage)}%" - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryScrollableHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryScrollableHeader.kt deleted file mode 100644 index c258f71d2..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryScrollableHeader.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.summary - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.scrollable_header_attendance_summary.* - -class AttendanceSummaryScrollableHeader(private val percentage: String) : - AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.scrollable_header_attendance_summary - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder?, position: Int, payloads: MutableList?) { - holder?.apply { attendanceSummaryScrollableHeaderPercentage.text = percentage } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as AttendanceSummaryScrollableHeader - - if (percentage != other.percentage) return false - - return true - } - - override fun hashCode(): Int { - return percentage.hashCode() - } - - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt deleted file mode 100644 index e4c36db7b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.summary - -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface AttendanceSummaryView : BaseSessionView { - - val isViewEmpty: Boolean - - fun initView() - - fun hideRefresh() - - fun showContent(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showEmpty(show: Boolean) - - fun updateDataSet(data: List, header: AttendanceSummaryScrollableHeader) - - fun updateSubjects(data: ArrayList) - - fun showSubjects(show: Boolean) - - fun clearView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt deleted file mode 100644 index ed5092c96..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.modules.exam - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_exam.* - -class ExamDialog : DialogFragment() { - - private lateinit var exam: Exam - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(exam: Exam): ExamDialog { - return ExamDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - exam = getSerializable(ARGUMENT_KEY) as Exam - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_exam, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - examDialogSubjectValue.text = exam.subject - examDialogTypeValue.text = exam.type - examDialogTeacherValue.text = exam.teacher - examDialogDateValue.text = exam.entryDate.toFormattedString() - examDialogDescriptionValue.text = exam.description - - examDialogClose.setOnClickListener { dismiss() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt deleted file mode 100644 index 97e97727f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt +++ /dev/null @@ -1,131 +0,0 @@ -package io.github.wulkanowy.ui.modules.exam - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_exam.* -import javax.inject.Inject - -class ExamFragment : BaseSessionFragment(), ExamView, MainView.MainChildView, MainView.TitledView { - - @Inject - lateinit var presenter: ExamPresenter - - @Inject - lateinit var examAdapter: FlexibleAdapter> - - companion object { - private const val SAVED_DATE_KEY = "CURRENT_DATE" - - fun newInstance() = ExamFragment() - } - - override val titleStringId: Int - get() = R.string.exam_title - - override val isViewEmpty: Boolean - get() = examAdapter.isEmpty - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_exam, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = examRecycler - presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) - } - - override fun initView() { - examAdapter.run { - setOnItemClickListener { presenter.onExamItemSelected(it) } - } - examRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = examAdapter - addItemDecoration(FlexibleItemDecoration(context) - .withDefaultDivider(R.layout.item_exam) - .withDrawDividerOnLastItem(false) - ) - } - examSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - examPreviousButton.setOnClickListener { presenter.onPreviousWeek() } - examNextButton.setOnClickListener { presenter.onNextWeek() } - } - - override fun hideRefresh() { - examSwipe.isRefreshing = false - } - - override fun updateData(data: List) { - examAdapter.updateDataSet(data, true) - } - - override fun updateNavigationWeek(date: String) { - examNavDate.text = date - } - - override fun clearData() { - examAdapter.clear() - } - - override fun resetView() { - examRecycler.scrollToPosition(0) - } - - override fun onFragmentReselected() { - presenter.onViewReselected() - } - - override fun showEmpty(show: Boolean) { - examEmpty.visibility = if (show) VISIBLE else GONE - } - - override fun showProgress(show: Boolean) { - examProgress.visibility = if (show) VISIBLE else GONE - } - - override fun enableSwipe(enable: Boolean) { - examSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - examRecycler.visibility = if (show) VISIBLE else GONE - } - - override fun showPreButton(show: Boolean) { - examPreviousButton.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showNextButton(show: Boolean) { - examNextButton.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showExamDialog(exam: Exam) { - (activity as? MainActivity)?.showDialogFragment(ExamDialog.newInstance(exam)) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamHeader.kt deleted file mode 100644 index 0a5b862c3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamHeader.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.ui.modules.exam - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractHeaderItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.ExpandableViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.utils.toFormattedString -import io.github.wulkanowy.utils.weekDayName -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.header_exam.* -import org.threeten.bp.LocalDate - -class ExamHeader(private val date: LocalDate) : AbstractHeaderItem() { - - override fun getLayoutRes() = R.layout.header_exam - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder, - position: Int, payloads: MutableList?) { - holder.run { - examHeaderDay.text = date.weekDayName.capitalize() - examHeaderDate.text = date.toFormattedString() - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as ExamHeader - - if (date != other.date) return false - - return true - } - - override fun hashCode(): Int { - return date.hashCode() - } - - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : ExpandableViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt deleted file mode 100644 index 8971b4df3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.modules.exam - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractSectionableItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Exam -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_exam.* - -class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem(header) { - - override fun getLayoutRes() = R.layout.item_exam - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.run { - examItemSubject.text = exam.subject - examItemTeacher.text = exam.teacher - examItemType.text = exam.type - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as ExamItem - - if (exam != other.exam) return false - - return true - } - - override fun hashCode(): Int { - var result = exam.hashCode() - result = 31 * result + exam.id.toInt() - return result - } - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt deleted file mode 100644 index 4b6ef298b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt +++ /dev/null @@ -1,136 +0,0 @@ -package io.github.wulkanowy.ui.modules.exam - -import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.repositories.exam.ExamRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.isHolidays -import io.github.wulkanowy.utils.monday -import io.github.wulkanowy.utils.nextOrSameSchoolDay -import io.github.wulkanowy.utils.toFormattedString -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDate.now -import org.threeten.bp.LocalDate.ofEpochDay -import timber.log.Timber -import java.util.concurrent.TimeUnit.MILLISECONDS -import javax.inject.Inject - -class ExamPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val examRepository: ExamRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - lateinit var currentDate: LocalDate - private set - - fun onAttachView(view: ExamView, date: Long?) { - super.onAttachView(view) - Timber.i("Exam view is attached") - view.initView() - loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay())) - reloadView() - } - - fun onPreviousWeek() { - loadData(currentDate.minusDays(7)) - reloadView() - } - - fun onNextWeek() { - loadData(currentDate.plusDays(7)) - reloadView() - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the exam") - loadData(currentDate, true) - } - - fun onExamItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is ExamItem) { - Timber.i("Select exam item ${item.exam.id}") - view?.showExamDialog(item.exam) - } - } - - fun onViewReselected() { - Timber.i("Exam view is reselected") - now().nextOrSameSchoolDay.also { - if (currentDate != it) { - loadData(it) - reloadView() - } else if (view?.isViewEmpty == false) view?.resetView() - } - } - - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - Timber.i("Loading exam data started") - currentDate = date - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .delay(200, MILLISECONDS) - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { - examRepository.getExams(it, currentDate.monday, currentDate.friday, forceRefresh) - }.map { it.groupBy { exam -> exam.date }.toSortedMap() } - .map { createExamItems(it) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - } - } - .subscribe({ - Timber.i("Loading exam result: Success") - view?.apply { - updateData(it) - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - } - analytics.logEvent("load_exam", "items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd")) - }) { - Timber.i("Loading exam result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } - } - - private fun createExamItems(items: Map>): List { - return items.flatMap { - ExamHeader(it.key).let { header -> - it.value.reversed().map { item -> ExamItem(header, item) } - } - } - } - - private fun reloadView() { - Timber.i("Reload exam view with the date ${currentDate.toFormattedString()}") - view?.apply { - showProgress(true) - enableSwipe(false) - showContent(false) - showEmpty(false) - clearData() - showPreButton(!currentDate.minusDays(7).isHolidays) - showNextButton(!currentDate.plusDays(7).isHolidays) - updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " + - currentDate.friday.toFormattedString("dd.MM")) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt deleted file mode 100644 index 2ced3f2d4..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.ui.modules.exam - -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface ExamView : BaseSessionView { - - val isViewEmpty: Boolean - - fun initView() - - fun updateData(data: List) - - fun updateNavigationWeek(date: String) - - fun clearData() - - fun hideRefresh() - - fun resetView() - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun showNextButton(show: Boolean) - - fun showPreButton(show: Boolean) - - fun showExamDialog(exam: Exam) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/CustomTabLayout.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/CustomTabLayout.kt deleted file mode 100644 index e6f01497c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/CustomTabLayout.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade - -import android.content.Context -import android.util.AttributeSet -import android.view.ViewGroup -import com.google.android.material.tabs.TabLayout - -/** - * @see Tabs don't fit to screen with tabmode=scrollable, Even with a Custom Tab Layout - */ -class CustomTabLayout : TabLayout { - - constructor(context: Context) : super(context) - - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) - - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - - override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - setMeasuredDimension(widthMeasureSpec, heightMeasureSpec) - val tabLayout = getChildAt(0) as ViewGroup - val childCount = tabLayout.childCount - - if (childCount == 0) return - - val tabMinWidth = context.resources.displayMetrics.widthPixels / childCount - - for (i in 0 until childCount) { - tabLayout.getChildAt(i).minimumWidth = tabMinWidth - } - - super.onMeasure(widthMeasureSpec, heightMeasureSpec) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt deleted file mode 100644 index 20d3fad70..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt +++ /dev/null @@ -1,164 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import androidx.appcompat.app.AlertDialog -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment -import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment -import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnSelectPageListener -import kotlinx.android.synthetic.main.fragment_grade.* -import javax.inject.Inject - -class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView, MainView.TitledView { - - @Inject - lateinit var presenter: GradePresenter - - @Inject - lateinit var pagerAdapter: BaseFragmentPagerAdapter - - private var semesterSwitchMenu: MenuItem? = null - - companion object { - private const val SAVED_SEMESTER_KEY = "CURRENT_SEMESTER" - - fun newInstance() = GradeFragment() - } - - override val titleStringId: Int - get() = R.string.grade_title - - override val currentPageIndex: Int - get() = gradeViewPager.currentItem - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_grade, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SEMESTER_KEY)) - } - - override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { - inflater?.inflate(R.menu.action_menu_grade, menu) - semesterSwitchMenu = menu?.findItem(R.id.gradeMenuSemester) - presenter.onCreateMenu() - } - - override fun initView() { - pagerAdapter.apply { - containerId = gradeViewPager.id - addFragmentsWithTitle(mapOf( - GradeDetailsFragment.newInstance() to getString(R.string.all_details), - GradeSummaryFragment.newInstance() to getString(R.string.grade_menu_summary), - GradeStatisticsFragment.newInstance() to getString(R.string.grade_menu_statistics) - )) - } - - gradeViewPager.run { - adapter = pagerAdapter - offscreenPageLimit = 3 - setOnSelectPageListener { presenter.onPageSelected(it) } - } - gradeTabLayout.setupWithViewPager(gradeViewPager) - gradeSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - return if (item?.itemId == R.id.gradeMenuSemester) presenter.onSemesterSwitch() - else false - } - - override fun onFragmentReselected() { - presenter.onViewReselected() - } - - override fun showContent(show: Boolean) { - gradeViewPager.visibility = if (show) VISIBLE else INVISIBLE - gradeTabLayout.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showProgress(show: Boolean) { - gradeProgress.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showEmpty(show: Boolean) { - gradeEmpty.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showRefresh(show: Boolean) { - gradeSwipe.isRefreshing = show - } - - override fun showSemesterSwitch(show: Boolean) { - semesterSwitchMenu?.isVisible = show - } - - override fun enableSwipe(enable: Boolean) { - gradeSwipe.isEnabled = enable - } - - override fun showSemesterDialog(selectedIndex: Int) { - arrayOf(getString(R.string.grade_semester, 1), - getString(R.string.grade_semester, 2)).also { array -> - context?.let { - AlertDialog.Builder(it) - .setSingleChoiceItems(array, selectedIndex) { dialog, which -> - presenter.onSemesterSelected(which) - dialog.dismiss() - } - .setTitle(R.string.grade_switch_semester) - .setNegativeButton(android.R.string.cancel) { _, _ -> } - .show() - } - } - } - - fun onChildRefresh() { - presenter.onChildViewRefresh() - } - - fun onChildFragmentLoaded(semesterId: Int) { - presenter.onChildViewLoaded(semesterId) - } - - override fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) { - (pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentLoadData(semesterId, forceRefresh) - } - - override fun notifyChildParentReselected(index: Int) { - (pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentReselected() - } - - override fun notifyChildSemesterChange(index: Int) { - (pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester() - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putInt(SAVED_SEMESTER_KEY, presenter.selectedIndex) - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeModule.kt deleted file mode 100644 index 46a923d9a..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeModule.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade - -import dagger.Module -import dagger.Provides -import dagger.android.ContributesAndroidInjector -import io.github.wulkanowy.di.scopes.PerChildFragment -import io.github.wulkanowy.di.scopes.PerFragment -import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment -import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment -import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment - -@Module -abstract class GradeModule { - - @Module - companion object { - - @JvmStatic - @PerFragment - @Provides - fun provideGradeAdapter(fragment: GradeFragment) = BaseFragmentPagerAdapter(fragment.childFragmentManager) - } - - @PerChildFragment - @ContributesAndroidInjector - abstract fun bindGradeDetailsFragment(): GradeDetailsFragment - - @PerChildFragment - @ContributesAndroidInjector - abstract fun binGradeSummaryFragment(): GradeSummaryFragment - - @PerChildFragment - @ContributesAndroidInjector - abstract fun binGradeStatisticsFragment(): GradeStatisticsFragment -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt deleted file mode 100644 index 2357e0637..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt +++ /dev/null @@ -1,130 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import timber.log.Timber -import javax.inject.Inject - -class GradePresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - var selectedIndex = 0 - private set - - private var semesters = emptyList() - - private val loadedSemesterId = mutableMapOf() - - fun onAttachView(view: GradeView, savedIndex: Int?) { - super.onAttachView(view) - Timber.i("Grade view is attached") - selectedIndex = savedIndex ?: 0 - view.run { - initView() - enableSwipe(false) - } - loadData() - } - - fun onCreateMenu() { - if (semesters.isEmpty()) view?.showSemesterSwitch(false) - } - - fun onViewReselected() { - Timber.i("Grade view is reselected") - view?.run { notifyChildParentReselected(currentPageIndex) } - } - - fun onSemesterSwitch(): Boolean { - if (semesters.isNotEmpty()) view?.showSemesterDialog(selectedIndex - 1) - return true - } - - fun onSemesterSelected(index: Int) { - if (selectedIndex != index - 1) { - Timber.i("Change semester in grade view to ${index + 1}") - selectedIndex = index + 1 - loadedSemesterId.clear() - view?.let { - notifyChildrenSemesterChange() - loadChild(it.currentPageIndex) - } - analytics.logEvent("changed_semester", "number" to index + 1) - } - } - - fun onChildViewRefresh() { - view?.let { loadChild(it.currentPageIndex, true) } - } - - fun onChildViewLoaded(semesterId: Int) { - view?.apply { - showContent(true) - showProgress(false) - showEmpty(false) - loadedSemesterId[currentPageIndex] = semesterId - } - } - - fun onPageSelected(index: Int) { - if (semesters.isNotEmpty()) loadChild(index) - } - - fun onSwipeRefresh() { - loadData() - } - - private fun loadData() { - Timber.i("Loading grade data started") - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it) } - .doOnSuccess { - it.first { item -> item.isCurrent }.also { current -> - selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex - semesters = it.filter { semester -> semester.diaryId == current.diaryId } - } - } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { view?.showRefresh(false) } - .subscribe({ - view?.run { - Timber.i("Loading grade result: Attempt load index $currentPageIndex") - loadChild(currentPageIndex) - enableSwipe(false) - showSemesterSwitch(true) - } - }) { - Timber.i("Loading grade result: An exception occurred") - errorHandler.dispatch(it) - view?.run { - showProgress(false) - showEmpty(true) - enableSwipe(true) - } - }) - } - - private fun loadChild(index: Int, forceRefresh: Boolean = false) { - semesters.first { it.semesterName == selectedIndex }.semesterId.also { - if (forceRefresh || loadedSemesterId[index] != it) { - Timber.i("Load grade child view index: $index") - view?.notifyChildLoadData(index, it, forceRefresh) - } - } - } - - private fun notifyChildrenSemesterChange() { - for (i in 0..2) view?.notifyChildSemesterChange(i) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt deleted file mode 100644 index 9fdd46b16..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade - -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface GradeView : BaseSessionView { - - val currentPageIndex: Int - - fun initView() - - fun showContent(show: Boolean) - - fun showProgress(show: Boolean) - - fun showEmpty(show: Boolean) - - fun showRefresh(show: Boolean) - - fun showSemesterSwitch(show: Boolean) - - fun showSemesterDialog(selectedIndex: Int) - - fun enableSwipe(enable: Boolean) - - fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) - - fun notifyChildParentReselected(index: Int) - - fun notifyChildSemesterChange(index: Int) - - interface GradeChildView { - - fun onParentChangeSemester() - - fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) - - fun onParentReselected() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt deleted file mode 100644 index 0abeaeea0..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt +++ /dev/null @@ -1,84 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.utils.colorStringId -import io.github.wulkanowy.utils.getBackgroundColor -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_grade.* - -class GradeDetailsDialog : DialogFragment() { - - private lateinit var grade: Grade - - private lateinit var colorScheme: String - - companion object { - private const val ARGUMENT_KEY = "Item" - private const val COLOR_SCHEME_KEY = "Scheme" - - fun newInstance(grade: Grade, colorScheme: String): GradeDetailsDialog { - return GradeDetailsDialog().apply { - arguments = Bundle().apply { - putSerializable(ARGUMENT_KEY, grade) - putString(COLOR_SCHEME_KEY, colorScheme) - } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - grade = getSerializable(ARGUMENT_KEY) as Grade - colorScheme = getString(COLOR_SCHEME_KEY) ?: "default" - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_grade, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - gradeDialogSubject.text = grade.subject - gradeDialogWeightValue.text = grade.weight - gradeDialogDateValue.text = grade.date.toFormattedString() - gradeDialogColorValue.text = getString(grade.colorStringId) - - gradeDialogCommentValue.apply { - if (grade.comment.isBlank()) { - visibility = GONE - gradeDialogComment.visibility = GONE - } else text = grade.comment - } - - gradeDialogValue.run { - text = grade.entry - setBackgroundResource(grade.getBackgroundColor(colorScheme)) - } - - gradeDialogTeacherValue.text = if (grade.teacher.isBlank()) { - getString(R.string.all_no_data) - } else grade.teacher - - gradeDialogDescriptionValue.text = grade.run { - when { - description.isBlank() && gradeSymbol.isNotBlank() -> gradeSymbol - description.isBlank() && gradeSymbol.isBlank() -> getString(R.string.all_no_description) - gradeSymbol.isNotBlank() && description.isNotBlank() -> "$gradeSymbol - $description" - else -> description - } - } - - gradeDialogClose.setOnClickListener { dismiss() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt deleted file mode 100644 index 205cde771..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt +++ /dev/null @@ -1,172 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.View.GONE -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IExpandable -import eu.davidea.flexibleadapter.items.IFlexible -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.grade.GradeFragment -import io.github.wulkanowy.ui.modules.grade.GradeView -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_grade_details.* -import javax.inject.Inject - -class GradeDetailsFragment : BaseSessionFragment(), GradeDetailsView, GradeView.GradeChildView { - - @Inject - lateinit var presenter: GradeDetailsPresenter - - @Inject - lateinit var gradeDetailsAdapter: FlexibleAdapter> - - companion object { - fun newInstance() = GradeDetailsFragment() - } - - override val emptyAverageString: String - get() = getString(R.string.grade_no_average) - - override val averageString: String - get() = getString(R.string.grade_average) - - override val weightString: String - get() = getString(R.string.grade_weight) - - override val noDescriptionString: String - get() = getString(R.string.all_no_description) - - override val isViewEmpty - get() = gradeDetailsAdapter.isEmpty - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_grade_details, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = gradeDetailsRecycler - presenter.onAttachView(this) - } - - override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { - inflater?.inflate(R.menu.action_menu_grade_details, menu) - } - - override fun initView() { - gradeDetailsAdapter.run { - isAutoCollapseOnExpand = true - isAutoScrollOnExpand = true - setOnItemClickListener { presenter.onGradeItemSelected(it) } - } - - gradeDetailsRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = gradeDetailsAdapter - addItemDecoration(GradeDetailsHeaderItemDecoration(context) - .withDefaultDivider(R.layout.header_grade_details) - ) - } - gradeDetailsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - return if (item?.itemId == R.id.gradeDetailsMenuRead) presenter.onMarkAsReadSelected() - else false - } - - override fun updateData(data: List) { - gradeDetailsAdapter.updateDataSet(data, true) - } - - override fun updateItem(item: AbstractFlexibleItem<*>) { - gradeDetailsAdapter.updateItem(item) - } - - override fun clearView() { - gradeDetailsAdapter.clear() - } - - override fun collapseAllItems() { - gradeDetailsAdapter.collapseAll() - } - - override fun scrollToStart() { - gradeDetailsRecycler.scrollToPosition(0) - } - - override fun getHeaderOfItem(item: AbstractFlexibleItem<*>): IExpandable<*, out IFlexible<*>>? { - return gradeDetailsAdapter.getExpandableOf(item) - } - - override fun getGradeNumberString(number: Int): String { - return resources.getQuantityString(R.plurals.grade_number_item, number, number) - } - - override fun showProgress(show: Boolean) { - gradeDetailsProgress.visibility = if (show) VISIBLE else GONE - } - - override fun enableSwipe(enable: Boolean) { - gradeDetailsSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - gradeDetailsRecycler.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showEmpty(show: Boolean) { - gradeDetailsEmpty.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showRefresh(show: Boolean) { - gradeDetailsSwipe.isRefreshing = show - } - - override fun showGradeDialog(grade: Grade, colorScheme: String) { - (activity as? MainActivity)?.showDialogFragment(GradeDetailsDialog.newInstance(grade, colorScheme)) - } - - override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { - presenter.onParentViewLoadData(semesterId, forceRefresh) - } - - override fun onParentReselected() { - presenter.onParentViewReselected() - } - - override fun onParentChangeSemester() { - presenter.onParentViewChangeSemester() - } - - override fun notifyParentDataLoaded(semesterId: Int) { - (parentFragment as? GradeFragment)?.onChildFragmentLoaded(semesterId) - } - - override fun notifyParentRefresh() { - (parentFragment as? GradeFragment)?.onChildRefresh() - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt deleted file mode 100644 index e5f6e8249..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt +++ /dev/null @@ -1,90 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractExpandableItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.ExpandableViewHolder -import io.github.wulkanowy.R -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.header_grade_details.* - -class GradeDetailsHeader( - private val subject: String, - private val number: String, - private val average: String, - var newGrades: Int, - private val isExpandable: Boolean -) : AbstractExpandableItem() { - - init { - isExpanded = !isExpandable - } - - override fun getLayoutRes() = R.layout.header_grade_details - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.run { - gradeHeaderSubject.apply { - text = subject - maxLines = if (isExpanded) 2 else 1 - } - gradeHeaderAverage.text = average - gradeHeaderNumber.text = number - gradeHeaderNote.visibility = if (newGrades > 0) VISIBLE else GONE - gradeHeaderContainer.isEnabled = isExpandable - - isViewExpandable = isExpandable - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as GradeDetailsHeader - - if (subject != other.subject) return false - if (number != other.number) return false - if (average != other.average) return false - if (isExpandable != other.isExpandable) return false - - return true - } - - override fun hashCode(): Int { - var result = subject.hashCode() - result = 31 * result + number.hashCode() - result = 31 * result + average.hashCode() - result = 31 * result + isExpandable.hashCode() - return result - } - - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : - ExpandableViewHolder(view, adapter), LayoutContainer { - - var isViewExpandable = true - - init { - contentView.setOnClickListener(this) - } - - override val containerView: View - get() = contentView - - override fun isViewCollapsibleOnClick() = isViewExpandable - - override fun isViewExpandableOnClick() = isViewExpandable - - override fun onClick(view: View?) { - super.onClick(view) - mAdapter.getItem(adapterPosition)?.let { mAdapter.updateItem(it) } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeaderItemDecoration.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeaderItemDecoration.kt deleted file mode 100644 index 39a911e62..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeaderItemDecoration.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import android.content.Context -import android.graphics.Canvas -import androidx.recyclerview.widget.RecyclerView -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration - -class GradeDetailsHeaderItemDecoration(context: Context) : FlexibleItemDecoration(context) { - - override fun drawVertical(canvas: Canvas, parent: RecyclerView) { - canvas.save() - val left: Int - val right: Int - if (parent.clipToPadding) { - left = parent.paddingLeft - right = parent.width - parent.paddingRight - canvas.clipRect(left, parent.paddingTop, right, - parent.height - parent.paddingBottom) - } else { - left = 0 - right = parent.width - } - - val itemCount = parent.childCount - for (i in 1 until itemCount) { - val child = parent.getChildAt(i) - val viewHolder = parent.getChildViewHolder(child) - if (shouldDrawDivider(viewHolder)) { - parent.getDecoratedBoundsWithMargins(child, mBounds) - val bottom = mBounds.top + Math.round(child.translationY) - val top = bottom - mDivider.intrinsicHeight - mDivider.setBounds(left, top, right, bottom) - mDivider.draw(canvas) - } - } - canvas.restore() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt deleted file mode 100644 index 1e47eca5d..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt +++ /dev/null @@ -1,74 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import android.annotation.SuppressLint -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_grade_details.* - -class GradeDetailsItem( - val grade: Grade, - private val valueBgColor: Int, - private val weightString: String, - private val noDescriptionString: String -) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_grade_details - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.run { - gradeItemValue.run { - text = grade.entry - setBackgroundResource(valueBgColor) - } - gradeItemDescription.text = when { - grade.description.isNotBlank() -> grade.description - grade.gradeSymbol.isNotBlank() -> grade.gradeSymbol - else -> noDescriptionString - } - gradeItemDate.text = grade.date.toFormattedString() - gradeItemWeight.text = "$weightString: ${grade.weight}" - gradeItemNote.visibility = if (!grade.isRead) VISIBLE else GONE - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as GradeDetailsItem - - if (grade != other.grade) return false - if (grade.id != other.grade.id) return false - if (weightString != other.weightString) return false - if (valueBgColor != other.valueBgColor) return false - - return true - } - - override fun hashCode(): Int { - var result = grade.hashCode() - result = 31 * result + grade.id.toInt() - result = 31 * result + weightString.hashCode() - result = 31 * result + valueBgColor - return result - } - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt deleted file mode 100644 index 1be0b36f9..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ /dev/null @@ -1,183 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.repositories.grade.GradeRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.calcAverage -import io.github.wulkanowy.utils.changeModifier -import io.github.wulkanowy.utils.getBackgroundColor -import timber.log.Timber -import javax.inject.Inject - -class GradeDetailsPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val gradeRepository: GradeRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val preferencesRepository: PreferencesRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - private var currentSemesterId = 0 - - override fun onAttachView(view: GradeDetailsView) { - super.onAttachView(view) - view.initView() - } - - fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { - currentSemesterId = semesterId - loadData(semesterId, forceRefresh) - } - - fun onGradeItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is GradeDetailsItem) { - Timber.i("Select grade item ${item.grade.id}") - view?.apply { - showGradeDialog(item.grade, preferencesRepository.gradeColorTheme) - if (!item.grade.isRead) { - item.grade.isRead = true - updateItem(item) - getHeaderOfItem(item)?.let { header -> - if (header is GradeDetailsHeader) { - header.newGrades-- - updateItem(header) - } - } - updateGrade(item.grade) - } - } - } - } - - fun onMarkAsReadSelected(): Boolean { - Timber.i("Select mark grades as read") - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it) } - .flatMap { gradeRepository.getUnreadGrades(it.first { item -> item.semesterId == currentSemesterId }) } - .map { it.map { grade -> grade.apply { isRead = true } } } - .flatMapCompletable { - Timber.i("Mark as read ${it.size} grades") - gradeRepository.updateGrades(it) - } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ - Timber.i("Mark as read result: Success") - loadData(currentSemesterId, false) - }, { - Timber.i("Mark as read result: An exception occurred") - errorHandler.dispatch(it) - })) - return true - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the grade details") - view?.notifyParentRefresh() - } - - fun onParentViewReselected() { - view?.run { - if (!isViewEmpty) { - if (preferencesRepository.isGradeExpandable) collapseAllItems() - scrollToStart() - } - } - } - - fun onParentViewChangeSemester() { - view?.run { - showProgress(true) - enableSwipe(false) - showRefresh(false) - showContent(false) - showEmpty(false) - clearView() - } - disposable.clear() - } - - private fun loadData(semesterId: Int, forceRefresh: Boolean) { - Timber.i("Loading grade details data started") - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } } - .flatMap { gradeRepository.getGrades(it.second, it.first.first { item -> item.semesterId == semesterId }, forceRefresh) } - .map { it.sortedByDescending { grade -> grade.date } } - .map { it.map { item -> item.changeModifier(preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier) } } - .map { createGradeItems(it.groupBy { grade -> grade.subject }.toSortedMap()) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - showRefresh(false) - showProgress(false) - enableSwipe(true) - notifyParentDataLoaded(semesterId) - } - } - .subscribe({ - Timber.i("Loading grade details result: Success") - view?.run { - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - updateData(it) - } - analytics.logEvent("load_grade_details", "items" to it.size, "force_refresh" to forceRefresh) - }) { - Timber.i("Loading grade details result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } - - private fun createGradeItems(items: Map>): List { - return items.map { - it.value.calcAverage().let { average -> - GradeDetailsHeader( - subject = it.key, - average = formatAverage(average), - number = view?.getGradeNumberString(it.value.size).orEmpty(), - newGrades = it.value.filter { grade -> !grade.isRead }.size, - isExpandable = preferencesRepository.isGradeExpandable - ).apply { - subItems = it.value.map { item -> - GradeDetailsItem( - grade = item, - valueBgColor = item.getBackgroundColor(preferencesRepository.gradeColorTheme), - weightString = view?.weightString.orEmpty(), - noDescriptionString = view?.noDescriptionString.orEmpty() - ) - } - } - } - } - } - - private fun formatAverage(average: Double): String { - return view?.run { - if (average == 0.0) emptyAverageString - else averageString.format(average) - }.orEmpty() - } - - private fun updateGrade(grade: Grade) { - Timber.i("Attempt to update grade ${grade.id}") - disposable.add(gradeRepository.updateGrade(grade) - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ Timber.i("Update grade result: Success") }) - { error -> - Timber.i("Update grade result: An exception occurred") - errorHandler.dispatch(error) - }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt deleted file mode 100644 index 1fb98216b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IExpandable -import eu.davidea.flexibleadapter.items.IFlexible -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface GradeDetailsView : BaseSessionView { - - val isViewEmpty: Boolean - - val emptyAverageString: String - - val averageString: String - - val weightString: String - - val noDescriptionString: String - - fun initView() - - fun updateData(data: List) - - fun updateItem(item: AbstractFlexibleItem<*>) - - fun clearView() - - fun scrollToStart() - - fun collapseAllItems() - - fun showGradeDialog(grade: Grade, colorScheme: String) - - fun showContent(show: Boolean) - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showRefresh(show: Boolean) - - fun notifyParentDataLoaded(semesterId: Int) - - fun notifyParentRefresh() - - fun getGradeNumberString(number: Int): String - - fun getHeaderOfItem(item: AbstractFlexibleItem<*>): IExpandable<*, out IFlexible<*>>? -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt deleted file mode 100644 index f27a13c83..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt +++ /dev/null @@ -1,214 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.statistics - -import android.graphics.Color.WHITE -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ArrayAdapter -import android.widget.TextView -import androidx.core.content.ContextCompat -import com.github.mikephil.charting.components.Legend -import com.github.mikephil.charting.components.LegendEntry -import com.github.mikephil.charting.data.PieData -import com.github.mikephil.charting.data.PieDataSet -import com.github.mikephil.charting.data.PieEntry -import com.github.mikephil.charting.formatter.ValueFormatter -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.grade.GradeFragment -import io.github.wulkanowy.ui.modules.grade.GradeView -import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.setOnItemSelectedListener -import kotlinx.android.synthetic.main.fragment_grade_statistics.* -import javax.inject.Inject - -class GradeStatisticsFragment : BaseSessionFragment(), GradeStatisticsView, GradeView.GradeChildView { - - @Inject - lateinit var presenter: GradeStatisticsPresenter - - private lateinit var subjectsAdapter: ArrayAdapter - - companion object { - private const val SAVED_CHART_TYPE = "CURRENT_TYPE" - - fun newInstance() = GradeStatisticsFragment() - } - - override val isViewEmpty - get() = gradeStatisticsChart.isEmpty - - private lateinit var gradeColors: List> - - private val vulcanGradeColors = listOf( - 6 to R.color.grade_vulcan_six, - 5 to R.color.grade_vulcan_five, - 4 to R.color.grade_vulcan_four, - 3 to R.color.grade_vulcan_three, - 2 to R.color.grade_vulcan_two, - 1 to R.color.grade_vulcan_one - ) - - private val materialGradeColors = listOf( - 6 to R.color.grade_material_six, - 5 to R.color.grade_material_five, - 4 to R.color.grade_material_four, - 3 to R.color.grade_material_three, - 2 to R.color.grade_material_two, - 1 to R.color.grade_material_one - ) - - private val gradeLabels = listOf( - "6, 6-", "5, 5-, 5+", "4, 4-, 4+", "3, 3-, 3+", "2, 2-, 2+", "1, 1+" - ) - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_grade_statistics, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = gradeStatisticsChart - presenter.onAttachView(this, savedInstanceState?.getBoolean(SAVED_CHART_TYPE)) - } - - override fun initView() { - gradeStatisticsChart.run { - description.isEnabled = false - setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground)) - setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary)) - animateXY(1000, 1000) - minAngleForSlices = 25f - legend.apply { - textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary) - } - } - - context?.let { - subjectsAdapter = ArrayAdapter(it, android.R.layout.simple_spinner_item, ArrayList()) - subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject) - } - - gradeStatisticsSubjects.run { - adapter = subjectsAdapter - setOnItemSelectedListener { presenter.onSubjectSelected((it as TextView).text.toString()) } - } - - gradeStatisticsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun updateSubjects(data: ArrayList) { - subjectsAdapter.run { - clear() - addAll(data) - notifyDataSetChanged() - } - } - - override fun updateData(items: List, theme: String) { - gradeColors = when (theme) { - "vulcan" -> vulcanGradeColors - else -> materialGradeColors - } - - gradeStatisticsChart.run { - data = PieData(PieDataSet(items.map { - PieEntry(it.amount.toFloat(), it.grade.toString()) - }, "Legenda").apply { - valueTextSize = 12f - sliceSpace = 1f - valueTextColor = WHITE - setColors(items.map { - gradeColors.single { color -> color.first == it.grade }.second - }.toIntArray(), context) - }).apply { - setTouchEnabled(false) - setValueFormatter(object : ValueFormatter() { - override fun getPieLabel(value: Float, pieEntry: PieEntry): String { - return resources.getQuantityString(R.plurals.grade_number_item, value.toInt(), value.toInt()) - } - }) - centerText = items.fold(0) { acc, it -> acc + it.amount } - .let { resources.getQuantityString(R.plurals.grade_number_item, it, it) } - } - legend.apply { - setCustom(gradeLabels.mapIndexed { i, it -> - LegendEntry().apply { - label = it - formColor = ContextCompat.getColor(context, gradeColors[i].second) - form = Legend.LegendForm.SQUARE - } - }) - } - invalidate() - } - } - - override fun showSubjects(show: Boolean) { - gradeStatisticsSubjectsContainer.visibility = if (show) View.VISIBLE else View.INVISIBLE - gradeStatisticsTypeSwitch.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun clearView() { - gradeStatisticsChart.clear() - } - - override fun showContent(show: Boolean) { - gradeStatisticsChart.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showEmpty(show: Boolean) { - gradeStatisticsEmpty.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showProgress(show: Boolean) { - gradeStatisticsProgress.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun enableSwipe(enable: Boolean) { - gradeStatisticsSwipe.isEnabled = enable - } - - override fun showRefresh(show: Boolean) { - gradeStatisticsSwipe.isRefreshing = show - } - - override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { - presenter.onParentViewLoadData(semesterId, forceRefresh) - } - - override fun onParentReselected() { - // - } - - override fun onParentChangeSemester() { - presenter.onParentViewChangeSemester() - } - - override fun notifyParentDataLoaded(semesterId: Int) { - (parentFragment as? GradeFragment)?.onChildFragmentLoaded(semesterId) - } - - override fun notifyParentRefresh() { - (parentFragment as? GradeFragment)?.onChildRefresh() - } - - override fun onResume() { - super.onResume() - gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId -> - presenter.onTypeChange(checkedId == R.id.gradeStatisticsTypeSemester) - } - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putBoolean(GradeStatisticsFragment.SAVED_CHART_TYPE, presenter.currentIsSemester) - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt deleted file mode 100644 index 5dd485cd5..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt +++ /dev/null @@ -1,147 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.statistics - -import io.github.wulkanowy.data.db.entities.Subject -import io.github.wulkanowy.data.repositories.gradestatistics.GradeStatisticsRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.subject.SubjectRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import timber.log.Timber -import javax.inject.Inject - -class GradeStatisticsPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val gradeStatisticsRepository: GradeStatisticsRepository, - private val subjectRepository: SubjectRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val preferencesRepository: PreferencesRepository, - private val schedulers: SchedulersProvider, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - private var subjects = emptyList() - - private var currentSemesterId = 0 - - private var currentSubjectName: String = "Wszystkie" - - var currentIsSemester = false - private set - - fun onAttachView(view: GradeStatisticsView, isSemester: Boolean?) { - super.onAttachView(view) - currentIsSemester = isSemester ?: false - view.initView() - } - - fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { - currentSemesterId = semesterId - loadSubjects() - loadData(semesterId, currentSubjectName, currentIsSemester, forceRefresh) - } - - fun onParentViewChangeSemester() { - view?.run { - showProgress(true) - enableSwipe(false) - showRefresh(false) - showContent(false) - showEmpty(false) - clearView() - } - disposable.clear() - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the grade stats") - view?.notifyParentRefresh() - } - - fun onSubjectSelected(name: String) { - Timber.i("Select grade stats subject $name") - view?.run { - showContent(false) - showProgress(true) - enableSwipe(false) - showEmpty(false) - clearView() - } - (subjects.singleOrNull { it.name == name }?.name).let { - if (it != currentSubjectName) loadData(currentSemesterId, name, currentIsSemester) - } - } - - fun onTypeChange(isSemester: Boolean) { - Timber.i("Select grade stats semester: $isSemester") - disposable.clear() - view?.run { - showContent(false) - showProgress(true) - enableSwipe(false) - showEmpty(false) - clearView() - } - loadData(currentSemesterId, currentSubjectName, isSemester) - } - - private fun loadSubjects() { - Timber.i("Loading grade stats subjects started") - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { subjectRepository.getSubjects(it) } - .doOnSuccess { subjects = it } - .map { ArrayList(it.map { subject -> subject.name }) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ - Timber.i("Loading grade stats subjects result: Success") - view?.run { - updateSubjects(it) - showSubjects(true) - } - }, { - Timber.e("Loading grade stats subjects result: An exception occurred") - errorHandler.dispatch(it) - }) - ) - } - - private fun loadData(semesterId: Int, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false) { - Timber.i("Loading grade stats data started") - currentSubjectName = subjectName - currentIsSemester = isSemester - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it) } - .flatMap { gradeStatisticsRepository.getGradesStatistics(it.first { item -> item.semesterId == semesterId }, subjectName, isSemester, forceRefresh) } - .map { list -> list.sortedByDescending { it.grade } } - .map { list -> list.filter { it.amount != 0 } } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - showRefresh(false) - showProgress(false) - enableSwipe(true) - notifyParentDataLoaded(semesterId) - } - } - .subscribe({ - Timber.i("Loading grade stats result: Success") - view?.run { - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - updateData(it, preferencesRepository.gradeColorTheme) - } - analytics.logEvent("load_grade_statistics", "items" to it.size, "force_refresh" to forceRefresh) - }) { - Timber.e("Loading grade stats result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt deleted file mode 100644 index dbdde459e..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.statistics - -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface GradeStatisticsView : BaseSessionView { - - val isViewEmpty: Boolean - - fun initView() - - fun updateSubjects(data: ArrayList) - - fun updateData(items: List, theme: String) - - fun showSubjects(show: Boolean) - - fun notifyParentDataLoaded(semesterId: Int) - - fun notifyParentRefresh() - - fun clearView() - - fun showContent(show: Boolean) - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showRefresh(show: Boolean) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt deleted file mode 100644 index f174f96c2..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt +++ /dev/null @@ -1,121 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.summary - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.grade.GradeFragment -import io.github.wulkanowy.ui.modules.grade.GradeView -import kotlinx.android.synthetic.main.fragment_grade_summary.* -import javax.inject.Inject - -class GradeSummaryFragment : BaseSessionFragment(), GradeSummaryView, GradeView.GradeChildView { - - @Inject - lateinit var presenter: GradeSummaryPresenter - - @Inject - lateinit var gradeSummaryAdapter: FlexibleAdapter> - - companion object { - fun newInstance() = GradeSummaryFragment() - } - - override val isViewEmpty - get() = gradeSummaryAdapter.isEmpty - - override val predictedString - get() = getString(R.string.grade_summary_predicted_grade) - - override val finalString - get() = getString(R.string.grade_summary_final_grade) - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_grade_summary, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = gradeSummaryRecycler - presenter.onAttachView(this) - } - - override fun initView() { - gradeSummaryAdapter.setDisplayHeadersAtStartUp(true) - - gradeSummaryRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = gradeSummaryAdapter - } - gradeSummarySwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun updateData(data: List, header: GradeSummaryScrollableHeader) { - gradeSummaryAdapter.apply { - updateDataSet(data, true) - removeAllScrollableHeaders() - addScrollableHeader(header) - } - } - - override fun clearView() { - gradeSummaryAdapter.clear() - } - - override fun resetView() { - gradeSummaryRecycler.scrollToPosition(0) - } - - override fun showContent(show: Boolean) { - gradeSummaryRecycler.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showEmpty(show: Boolean) { - gradeSummaryEmpty.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showProgress(show: Boolean) { - gradeSummaryProgress.visibility = if (show) VISIBLE else GONE - } - - override fun enableSwipe(enable: Boolean) { - gradeSummarySwipe.isEnabled = enable - } - - override fun showRefresh(show: Boolean) { - gradeSummarySwipe.isRefreshing = show - } - - override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { - presenter.onParentViewLoadData(semesterId, forceRefresh) - } - - override fun onParentReselected() { - presenter.onParentViewReselected() - } - - override fun onParentChangeSemester() { - presenter.onParentViewChangeSemester() - } - - override fun notifyParentDataLoaded(semesterId: Int) { - (parentFragment as? GradeFragment)?.onChildFragmentLoaded(semesterId) - } - - override fun notifyParentRefresh() { - (parentFragment as? GradeFragment)?.onChildRefresh() - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt deleted file mode 100644 index 5737a832f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.summary - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_grade_summary.* - -class GradeSummaryItem( - private val title: String, - private val average: String, - private val predictedGrade: String, - private val finalGrade: String -) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_grade_summary - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.run { - gradeSummaryItemTitle.text = title - gradeSummaryItemAverage.text = average - gradeSummaryItemPredicted.text = predictedGrade - gradeSummaryItemFinal.text = finalGrade - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as GradeSummaryItem - - if (average != other.average) return false - if (title != other.title) return false - if (predictedGrade != other.predictedGrade) return false - if (finalGrade != other.finalGrade) return false - - return true - } - - override fun hashCode(): Int { - var result = title.hashCode() - result = 31 * result + average.hashCode() - result = 31 * result + predictedGrade.hashCode() - result = 31 * result + finalGrade.hashCode() - return result - } - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt deleted file mode 100644 index 5dbf75134..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ /dev/null @@ -1,129 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.summary - -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.repositories.grade.GradeRepository -import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.calcAverage -import io.github.wulkanowy.utils.changeModifier -import timber.log.Timber -import java.lang.String.format -import java.util.Locale.FRANCE -import javax.inject.Inject - -class GradeSummaryPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val gradeSummaryRepository: GradeSummaryRepository, - private val gradeRepository: GradeRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val preferencesRepository: PreferencesRepository, - private val schedulers: SchedulersProvider, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - override fun onAttachView(view: GradeSummaryView) { - super.onAttachView(view) - view.initView() - } - - fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { - Timber.i("Loading grade summary data started") - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } } - .map { pair -> pair.first.first { it.semesterId == semesterId } to pair.second } - .flatMap { - gradeSummaryRepository.getGradesSummary(it.first, forceRefresh) - .flatMap { gradesSummary -> - gradeRepository.getGrades(it.second, it.first, forceRefresh) - .map { grades -> - grades.map { item -> item.changeModifier(preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier) } - .groupBy { grade -> grade.subject } - .mapValues { entry -> entry.value.calcAverage() } - .filterValues { value -> value != 0.0 } - .let { averages -> - createGradeSummaryItems(gradesSummary, averages) to - GradeSummaryScrollableHeader( - formatAverage(gradesSummary.calcAverage()), - formatAverage(averages.values.average()) - ) - } - } - } - } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - showRefresh(false) - showProgress(false) - enableSwipe(true) - notifyParentDataLoaded(semesterId) - } - }.subscribe({ - Timber.i("Loading grade summary result: Success") - view?.run { - showEmpty(it.first.isEmpty()) - showContent(it.first.isNotEmpty()) - updateData(it.first, it.second) - } - analytics.logEvent("load_grade_summary", "items" to it.first.size, "force_refresh" to forceRefresh) - }) { - Timber.i("Loading grade summary result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the grade summary") - view?.notifyParentRefresh() - } - - fun onParentViewReselected() { - view?.run { - if (!isViewEmpty) resetView() - } - } - - fun onParentViewChangeSemester() { - view?.run { - showProgress(true) - enableSwipe(false) - showRefresh(false) - showContent(false) - showEmpty(false) - clearView() - } - disposable.clear() - } - - private fun createGradeSummaryItems(gradesSummary: List, averages: Map) - : List { - return gradesSummary.filter { !checkEmpty(it, averages) }.map { it -> - GradeSummaryItem( - title = it.subject, - average = formatAverage(averages.getOrElse(it.subject) { 0.0 }, ""), - predictedGrade = it.predictedGrade, - finalGrade = it.finalGrade - ) - } - } - - private fun checkEmpty(gradeSummary: GradeSummary, averages: Map): Boolean { - return gradeSummary.run { - finalGrade.isBlank() && predictedGrade.isBlank() && averages[subject] == null - } - } - - private fun formatAverage(average: Double, defaultValue: String = "-- --"): String { - return if (average == 0.0) defaultValue - else format(FRANCE, "%.2f", average) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryScrollableHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryScrollableHeader.kt deleted file mode 100644 index f1c535c71..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryScrollableHeader.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.summary - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.scrollable_header_grade_summary.* - -class GradeSummaryScrollableHeader(private val finalAverage: String, private val calculatedAverage: String) - : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.scrollable_header_grade_summary - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder?, - position: Int, payloads: MutableList?) { - holder?.apply { - gradeSummaryScrollableHeaderFinal.text = finalAverage - gradeSummaryScrollableHeaderCalculated.text = calculatedAverage - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as GradeSummaryScrollableHeader - - if (calculatedAverage != other.calculatedAverage) return false - if (finalAverage != other.finalAverage) return false - - return true - } - - override fun hashCode(): Int { - var result = calculatedAverage.hashCode() - result = 31 * result + finalAverage.hashCode() - return result - } - - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt deleted file mode 100644 index 5f7c7b168..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.summary - -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface GradeSummaryView : BaseSessionView { - - val isViewEmpty: Boolean - - val predictedString: String - - val finalString: String - - fun initView() - - fun updateData(data: List, header: GradeSummaryScrollableHeader) - - fun resetView() - - fun clearView() - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showRefresh(show: Boolean) - - fun showContent(show: Boolean) - - fun showEmpty(show: Boolean) - - fun notifyParentDataLoaded(semesterId: Int) - - fun notifyParentRefresh() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkDialog.kt deleted file mode 100644 index 427841886..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkDialog.kt +++ /dev/null @@ -1,49 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_homework.* - -class HomeworkDialog : DialogFragment() { - - private lateinit var homework: Homework - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(homework: Homework): HomeworkDialog { - return HomeworkDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, homework) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - homework = getSerializable(HomeworkDialog.ARGUMENT_KEY) as Homework - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_homework, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - homeworkDialogDate.text = homework.date.toFormattedString() - homeworkDialogEntryDate.text = homework.entryDate.toFormattedString() - homeworkDialogSubject.text = homework.subject - homeworkDialogTeacher.text = homework.teacher - homeworkDialogContent.text = homework.content - homeworkDialogClose.setOnClickListener { dismiss() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt deleted file mode 100644 index 7325e3ecd..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt +++ /dev/null @@ -1,120 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_homework.* -import javax.inject.Inject - -class HomeworkFragment : BaseSessionFragment(), HomeworkView, MainView.TitledView { - - @Inject - lateinit var presenter: HomeworkPresenter - - @Inject - lateinit var homeworkAdapter: FlexibleAdapter> - - companion object { - private const val SAVED_DATE_KEY = "CURRENT_DATE" - - fun newInstance() = HomeworkFragment() - } - - override val titleStringId: Int - get() = R.string.homework_title - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_homework, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = homeworkRecycler - presenter.onAttachView(this, savedInstanceState?.getLong(HomeworkFragment.SAVED_DATE_KEY)) - } - - override fun initView() { - homeworkAdapter.run { - setOnItemClickListener { presenter.onHomeworkItemSelected(it) } - } - - homeworkRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = homeworkAdapter - addItemDecoration(FlexibleItemDecoration(context) - .withDefaultDivider() - .withDrawDividerOnLastItem(false) - ) - } - homeworkSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - homeworkPreviousButton.setOnClickListener { presenter.onPreviousDay() } - homeworkNextButton.setOnClickListener { presenter.onNextDay() } - } - - override fun updateData(data: List) { - homeworkAdapter.updateDataSet(data, true) - } - - override fun clearData() { - homeworkAdapter.clear() - } - - override fun updateNavigationWeek(date: String) { - homeworkNavDate.text = date - } - - override fun isViewEmpty() = homeworkAdapter.isEmpty - - override fun hideRefresh() { - homeworkSwipe.isRefreshing = false - } - - override fun showEmpty(show: Boolean) { - homeworkEmpty.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showProgress(show: Boolean) { - homeworkProgress.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun enableSwipe(enable: Boolean) { - homeworkSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - homeworkRecycler.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showPreButton(show: Boolean) { - homeworkPreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showNextButton(show: Boolean) { - homeworkNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showTimetableDialog(homework: Homework) { - (activity as? MainActivity)?.showDialogFragment(HomeworkDialog.newInstance(homework)) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putLong(HomeworkFragment.SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkHeader.kt deleted file mode 100644 index 490237883..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkHeader.kt +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractHeaderItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.ExpandableViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.utils.toFormattedString -import io.github.wulkanowy.utils.weekDayName -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.header_homework.* -import org.threeten.bp.LocalDate - -class HomeworkHeader(private val date: LocalDate) : AbstractHeaderItem() { - - override fun getLayoutRes() = R.layout.header_homework - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder( - adapter: FlexibleAdapter>?, holder: HomeworkHeader.ViewHolder, - position: Int, payloads: MutableList? - ) { - holder.run { - homeworkHeaderDay.text = date.weekDayName.capitalize() - homeworkHeaderDate.text = date.toFormattedString() - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as HomeworkHeader - - if (date != other.date) return false - - return true - } - - override fun hashCode(): Int { - return date.hashCode() - } - - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : ExpandableViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt deleted file mode 100644 index 2de9233f2..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractSectionableItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Homework -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_homework.* - -class HomeworkItem(header: HomeworkHeader, val homework: Homework) : - AbstractSectionableItem(header) { - - override fun getLayoutRes() = R.layout.item_homework - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - homeworkItemSubject.text = homework.subject - homeworkItemTeacher.text = homework.teacher - homeworkItemContent.text = homework.content - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as HomeworkItem - - if (homework != other.homework) return false - return true - } - - override fun hashCode(): Int { - var result = homework.hashCode() - result = 31 * result + homework.id.toInt() - return result - } - - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt deleted file mode 100644 index 739d44947..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt +++ /dev/null @@ -1,123 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.repositories.homework.HomeworkRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.isHolidays -import io.github.wulkanowy.utils.monday -import io.github.wulkanowy.utils.nextOrSameSchoolDay -import io.github.wulkanowy.utils.toFormattedString -import org.threeten.bp.LocalDate -import timber.log.Timber -import java.util.concurrent.TimeUnit -import javax.inject.Inject - -class HomeworkPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val homeworkRepository: HomeworkRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - lateinit var currentDate: LocalDate - private set - - fun onAttachView(view: HomeworkView, date: Long?) { - super.onAttachView(view) - Timber.i("Homework view is attached") - view.initView() - loadData(LocalDate.ofEpochDay(date ?: LocalDate.now().nextOrSameSchoolDay.toEpochDay())) - reloadView() - } - - fun onPreviousDay() { - loadData(currentDate.minusDays(7)) - reloadView() - } - - fun onNextDay() { - loadData(currentDate.plusDays(7)) - reloadView() - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the homework") - loadData(currentDate, true) - } - - fun onHomeworkItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is HomeworkItem) { - Timber.i("Select homework item ${item.homework.id}") - view?.showTimetableDialog(item.homework) - } - } - - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - Timber.i("Loading homework data started") - currentDate = date - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .delay(200, TimeUnit.MILLISECONDS) - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { homeworkRepository.getHomework(it, currentDate, currentDate, forceRefresh) } - .map { it.groupBy { homework -> homework.date }.toSortedMap() } - .map { createHomeworkItem(it) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - } - } - .subscribe({ - Timber.i("Loading homework result: Success") - view?.apply { - updateData(it) - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - } - analytics.logEvent("load_homework", "items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd")) - }) { - Timber.i("Loading homework result: An exception occurred") - view?.run { showEmpty(isViewEmpty()) } - errorHandler.dispatch(it) - }) - } - } - - private fun createHomeworkItem(items: Map>): List { - return items.flatMap { - HomeworkHeader(it.key).let { header -> - it.value.reversed().map { item -> HomeworkItem(header, item) } - } - } - } - - private fun reloadView() { - Timber.i("Reload homework view with the date ${currentDate.toFormattedString()}") - view?.apply { - showProgress(true) - enableSwipe(false) - showContent(false) - showEmpty(false) - clearData() - showNextButton(!currentDate.plusDays(7).isHolidays) - showPreButton(!currentDate.minusDays(7).isHolidays) - updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " + - currentDate.friday.toFormattedString("dd.MM")) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt deleted file mode 100644 index 546d0526f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface HomeworkView : BaseSessionView { - - fun initView() - - fun updateData(data: List) - - fun clearData() - - fun updateNavigationWeek(date: String) - - fun isViewEmpty(): Boolean - - fun hideRefresh() - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun showPreButton(show: Boolean) - - fun showNextButton(show: Boolean) - - fun showTimetableDialog(homework: Homework) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt deleted file mode 100644 index da054bb52..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BaseActivity -import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment -import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment -import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment -import io.github.wulkanowy.utils.setOnSelectPageListener -import kotlinx.android.synthetic.main.activity_login.* -import javax.inject.Inject - -class LoginActivity : BaseActivity(), LoginView { - - @Inject - lateinit var presenter: LoginPresenter - - @Inject - lateinit var loginAdapter: BaseFragmentPagerAdapter - - companion object { - fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java) - } - - override val currentViewIndex: Int - get() = loginViewpager.currentItem - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_login) - messageContainer = loginContainer - - presenter.onAttachView(this) - } - - override fun initAdapter() { - loginAdapter.apply { - containerId = loginViewpager.id - addFragments(listOf( - LoginFormFragment.newInstance(), - LoginSymbolFragment.newInstance(), - LoginStudentSelectFragment.newInstance() - )) - } - - loginViewpager.run { - offscreenPageLimit = 2 - adapter = loginAdapter - setOnSelectPageListener { presenter.onViewSelected(it) } - } - } - - override fun switchView(index: Int) { - loginViewpager.setCurrentItem(index, false) - } - - override fun showActionBar(show: Boolean) { - supportActionBar?.apply { if (show) show() else hide() } - } - - override fun onBackPressed() { - presenter.onBackPressed { super.onBackPressed() } - } - - override fun notifyInitSymbolFragment(loginData: Triple) { - (loginAdapter.getFragmentInstance(1) as? LoginSymbolFragment)?.onParentInitSymbolFragment(loginData) - } - - override fun notifyInitStudentSelectFragment(students: List) { - (loginAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment)?.onParentInitStudentSelectFragment(students) - } - - fun onFormFragmentAccountLogged(students: List, loginData: Triple) { - presenter.onFormViewAccountLogged(students, loginData) - } - - fun onSymbolFragmentAccountLogged(students: List) { - presenter.onSymbolViewAccountLogged(students) - } - - public override fun onDestroy() { - presenter.onDetachView() - super.onDestroy() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt deleted file mode 100644 index a70ff2d65..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import android.content.res.Resources -import android.database.sqlite.SQLiteConstraintException -import com.readystatesoftware.chuck.api.ChuckCollector -import io.github.wulkanowy.R -import io.github.wulkanowy.api.login.BadCredentialsException -import io.github.wulkanowy.ui.base.ErrorHandler -import javax.inject.Inject - -class LoginErrorHandler @Inject constructor( - resources: Resources, - chuckCollector: ChuckCollector -) : ErrorHandler(resources, chuckCollector) { - - var onBadCredentials: () -> Unit = {} - - var onStudentDuplicate: (String) -> Unit = {} - - override fun proceed(error: Throwable) { - when (error) { - is BadCredentialsException -> onBadCredentials() - is SQLiteConstraintException -> onStudentDuplicate(resources.getString(R.string.login_duplicate_student)) - else -> super.proceed(error) - } - } - - override fun clear() { - super.clear() - onBadCredentials = {} - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt deleted file mode 100644 index 7c3e7bace..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import dagger.Module -import dagger.Provides -import dagger.android.ContributesAndroidInjector -import io.github.wulkanowy.di.scopes.PerActivity -import io.github.wulkanowy.di.scopes.PerFragment -import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment -import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment -import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment - -@Module -internal abstract class LoginModule { - - @Module - companion object { - - @JvmStatic - @PerActivity - @Provides - fun provideLoginAdapter(activity: LoginActivity) = BaseFragmentPagerAdapter(activity.supportFragmentManager) - } - - @PerFragment - @ContributesAndroidInjector - abstract fun bindLoginFormFragment(): LoginFormFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindLoginSymbolFragment(): LoginSymbolFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindLoginSelectStudentFragment(): LoginStudentSelectFragment -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt deleted file mode 100644 index fb025beea..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import timber.log.Timber -import javax.inject.Inject - -class LoginPresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresenter(errorHandler) { - - override fun onAttachView(view: LoginView) { - super.onAttachView(view) - view.run { - initAdapter() - showActionBar(false) - } - Timber.i("Login view is attached") - } - - fun onFormViewAccountLogged(students: List, loginData: Triple) { - view?.apply { - if (students.isEmpty()) { - Timber.i("Switch to symbol form") - notifyInitSymbolFragment(loginData) - switchView(1) - } else { - Timber.i("Switch to student select") - notifyInitStudentSelectFragment(students) - switchView(2) - } - } - } - - fun onSymbolViewAccountLogged(students: List) { - view?.apply { - Timber.i("Switch to student select") - notifyInitStudentSelectFragment(students) - switchView(2) - } - } - - fun onViewSelected(index: Int) { - view?.apply { - when (index) { - 0, 1 -> showActionBar(false) - 2 -> showActionBar(true) - } - } - } - - fun onBackPressed(default: () -> Unit) { - Timber.i("Back pressed in login view") - view?.apply { - when (currentViewIndex) { - 1, 2 -> switchView(0) - else -> default() - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt deleted file mode 100644 index 58d356bbd..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BaseView - -interface LoginView : BaseView { - - val currentViewIndex: Int - - fun initAdapter() - - fun switchView(index: Int) - - fun showActionBar(show: Boolean) - - fun notifyInitSymbolFragment(loginData: Triple) - - fun notifyInitStudentSelectFragment(students: List) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt deleted file mode 100644 index 54fd0704b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt +++ /dev/null @@ -1,145 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.form - -import android.annotation.SuppressLint -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.view.ViewGroup -import android.view.inputmethod.EditorInfo.IME_ACTION_DONE -import android.view.inputmethod.EditorInfo.IME_NULL -import android.widget.ArrayAdapter -import io.github.wulkanowy.BuildConfig.VERSION_NAME -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.utils.hideSoftInput -import io.github.wulkanowy.utils.setOnItemSelectedListener -import io.github.wulkanowy.utils.setOnTextChangedListener -import io.github.wulkanowy.utils.showSoftInput -import kotlinx.android.synthetic.main.fragment_login_form.* -import javax.inject.Inject - -class LoginFormFragment : BaseFragment(), LoginFormView { - - @Inject - lateinit var presenter: LoginFormPresenter - - companion object { - fun newInstance() = LoginFormFragment() - } - - override val formNameValue: String - get() = loginFormName.text.toString() - - override val formPassValue: String - get() = loginFormPass.text.toString() - - override val formHostValue: String? - get() = resources.getStringArray(R.array.endpoints_values)[loginFormHost.selectedItemPosition] - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_login_form, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this) - } - - override fun initView() { - loginFormName.setOnTextChangedListener { presenter.onNameTextChanged() } - loginFormPass.setOnTextChangedListener { presenter.onPassTextChanged() } - loginFormHost.setOnItemSelectedListener { presenter.onHostSelected() } - loginFormSignIn.setOnClickListener { presenter.attemptLogin() } - - loginFormPass.setOnEditorActionListener { _, id, _ -> - if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false - } - - context?.let { - loginFormHost.adapter = ArrayAdapter.createFromResource(it, R.array.endpoints_keys, android.R.layout.simple_spinner_item) - .apply { setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) } - } - } - - override fun setDefaultCredentials(name: String, pass: String) { - loginFormName.setText(name) - loginFormPass.setText(pass) - } - - override fun setErrorNameRequired() { - loginFormNameLayout.run { - requestFocus() - error = getString(R.string.login_field_required) - } - } - - override fun setErrorPassRequired(focus: Boolean) { - loginFormPassLayout.run { - if (focus) requestFocus() - error = getString(R.string.login_field_required) - } - } - - override fun setErrorPassInvalid(focus: Boolean) { - loginFormPassLayout.run { - if (focus) requestFocus() - error = getString(R.string.login_invalid_password) - } - } - - override fun setErrorPassIncorrect() { - loginFormPassLayout.run { - requestFocus() - error = getString(R.string.login_incorrect_password) - } - } - - override fun clearNameError() { - loginFormNameLayout.error = null - } - - override fun clearPassError() { - loginFormPassLayout.error = null - } - - override fun showSoftKeyboard() { - activity?.showSoftInput() - } - - override fun hideSoftKeyboard() { - activity?.hideSoftInput() - } - - override fun showProgress(show: Boolean) { - loginFormProgress.visibility = if (show) VISIBLE else GONE - } - - override fun showContent(show: Boolean) { - loginFormContainer.visibility = if (show) VISIBLE else GONE - } - - @SuppressLint("SetTextI18n") - override fun showVersion() { - loginFormVersion.apply { - visibility = VISIBLE - text = "${getString(R.string.app_name)} $VERSION_NAME" - } - } - - override fun notifyParentAccountLogged(students: List) { - (activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple( - loginFormName.text.toString(), - loginFormPass.text.toString(), - resources.getStringArray(R.array.endpoints_values)[loginFormHost.selectedItemPosition] - )) - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt deleted file mode 100644 index a0717649a..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt +++ /dev/null @@ -1,104 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.form - -import com.google.firebase.analytics.FirebaseAnalytics.Param.SUCCESS -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import timber.log.Timber -import javax.inject.Inject -import javax.inject.Named - -class LoginFormPresenter @Inject constructor( - private val schedulers: SchedulersProvider, - private val errorHandler: LoginErrorHandler, - private val studentRepository: StudentRepository, - private val analytics: FirebaseAnalyticsHelper, - @param:Named("isDebug") private val isDebug: Boolean -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: LoginFormView) { - super.onAttachView(view) - view.run { - initView() - if (isDebug) showVersion() - errorHandler.onBadCredentials = { - setErrorPassIncorrect() - showSoftKeyboard() - Timber.i("Entered wrong username or password") - } - } - } - - fun onHostSelected() { - view?.apply { - clearPassError() - clearNameError() - if (formHostValue?.contains("fakelog") == true) setDefaultCredentials("jan@fakelog.cf", "jan123") - } - } - - fun onPassTextChanged() { - view?.clearPassError() - } - - fun onNameTextChanged() { - view?.clearNameError() - } - - fun attemptLogin() { - val email = view?.formNameValue.orEmpty() - val password = view?.formPassValue.orEmpty() - val endpoint = view?.formHostValue.orEmpty() - - if (!validateCredentials(email, password)) return - - disposable.add(studentRepository.getStudents(email, password, endpoint) - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doOnSubscribe { - view?.apply { - hideSoftKeyboard() - showProgress(true) - showContent(false) - } - Timber.i("Login started") - } - .doFinally { - view?.apply { - showProgress(false) - showContent(true) - } - } - .subscribe({ - Timber.i("Login result: Success") - analytics.logEvent("registration_form", SUCCESS to true, "students" to it.size, "endpoint" to endpoint, "error" to "No error") - view?.notifyParentAccountLogged(it) - }, { - Timber.i("Login result: An exception occurred") - analytics.logEvent("registration_form", SUCCESS to false, "students" to -1, "endpoint" to endpoint, "error" to it.localizedMessage) - errorHandler.dispatch(it) - })) - } - - private fun validateCredentials(login: String, password: String): Boolean { - var isCorrect = true - - if (login.isEmpty()) { - view?.setErrorNameRequired() - isCorrect = false - } - - if (password.isEmpty()) { - view?.setErrorPassRequired(focus = isCorrect) - isCorrect = false - } - - if (password.length < 6 && password.isNotEmpty()) { - view?.setErrorPassInvalid(focus = isCorrect) - isCorrect = false - } - return isCorrect - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt deleted file mode 100644 index 69672780d..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.form - -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BaseView - -interface LoginFormView : BaseView { - - fun initView() - - val formNameValue: String - - val formPassValue: String - - val formHostValue: String? - - fun setDefaultCredentials(name: String, pass: String) - - fun setErrorNameRequired() - - fun setErrorPassRequired(focus: Boolean) - - fun setErrorPassInvalid(focus: Boolean) - - fun setErrorPassIncorrect() - - fun clearNameError() - - fun clearPassError() - - fun showSoftKeyboard() - - fun hideSoftKeyboard() - - fun showProgress(show: Boolean) - - fun showContent(show: Boolean) - - fun showVersion() - - fun notifyParentAccountLogged(students: List) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt deleted file mode 100644 index 2bcb6b4e9..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt +++ /dev/null @@ -1,92 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.studentselect - -import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK -import android.content.Intent.FLAG_ACTIVITY_NEW_TASK -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_login_student_select.* -import java.io.Serializable -import javax.inject.Inject - -class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView { - - @Inject - lateinit var presenter: LoginStudentSelectPresenter - - @Inject - lateinit var loginAdapter: FlexibleAdapter> - - companion object { - const val SAVED_STUDENTS = "STUDENTS" - - fun newInstance() = LoginStudentSelectFragment() - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_login_student_select, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this, savedInstanceState?.getSerializable(SAVED_STUDENTS)) - } - - override fun initView() { - loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } } - - loginStudentSelectRecycler.apply { - adapter = loginAdapter - layoutManager = SmoothScrollLinearLayoutManager(context) - } - } - - override fun updateData(data: List) { - loginAdapter.updateDataSet(data, true) - } - - override fun openMainView() { - activity?.let { - startActivity(MainActivity.getStartIntent(it) - .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) }) - } - } - - override fun showProgress(show: Boolean) { - loginStudentSelectProgress.visibility = if (show) VISIBLE else GONE - } - - override fun showContent(show: Boolean) { - loginStudentSelectRecycler.visibility = if (show) VISIBLE else GONE - } - - override fun showActionBar(show: Boolean) { - (activity as? AppCompatActivity)?.supportActionBar?.run { if (show) show() else hide() } - } - - fun onParentInitStudentSelectFragment(students: List) { - presenter.onParentInitStudentSelectView(students) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putSerializable(SAVED_STUDENTS, presenter.students as Serializable) - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt deleted file mode 100644 index 71a7d681e..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.studentselect - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Student -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_login_student_select.* - -class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem() { - - override fun getLayoutRes(): Int = R.layout.item_login_student_select - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ItemViewHolder { - return ItemViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ItemViewHolder, position: Int, payloads: MutableList?) { - holder.run { - loginItemName.text = student.studentName - loginItemSchool.text = student.schoolName - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as LoginStudentSelectItem - - if (student != other.student) return false - - return true - } - - override fun hashCode(): Int { - return student.hashCode() - } - - class ItemViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = itemView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt deleted file mode 100644 index 6c2acb3bd..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt +++ /dev/null @@ -1,87 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.studentselect - -import com.google.firebase.analytics.FirebaseAnalytics.Param.SUCCESS -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.reactivex.Single -import timber.log.Timber -import java.io.Serializable -import javax.inject.Inject - -class LoginStudentSelectPresenter @Inject constructor( - private val errorHandler: LoginErrorHandler, - private val studentRepository: StudentRepository, - private val schedulers: SchedulersProvider, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - var students = emptyList() - - fun onAttachView(view: LoginStudentSelectView, students: Serializable?) { - super.onAttachView(view) - view.run { - initView() - errorHandler.onStudentDuplicate = { - showMessage(it) - Timber.i("The student already registered in the app was selected") - } - } - - if (students is List<*> && students.isNotEmpty()) { - loadData(students.filterIsInstance()) - } - } - - fun onParentInitStudentSelectView(students: List) { - loadData(students) - } - - fun onItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is LoginStudentSelectItem) { - registerStudent(item.student) - } - } - - private fun loadData(students: List) { - this.students = students - view?.apply { - updateData(students.map { LoginStudentSelectItem(it) }) - } - } - - private fun registerStudent(student: Student) { - disposable.add(studentRepository.saveStudent(student) - .map { student.apply { id = it } } - .onErrorResumeNext { studentRepository.logoutStudent(student).andThen(Single.error(it)) } - .flatMapCompletable { studentRepository.switchStudent(student) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doOnSubscribe { - view?.apply { - showProgress(true) - showContent(false) - showActionBar(false) - } - Timber.i("Registration started") - } - .subscribe({ - analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to "No error") - Timber.i("Registration result: Success") - view?.openMainView() - }, { - analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to it.localizedMessage) - Timber.i("Registration result: An exception occurred ") - errorHandler.dispatch(it) - view?.apply { - showProgress(false) - showContent(true) - showActionBar(true) - } - })) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt deleted file mode 100644 index 824f421fe..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.studentselect - -import io.github.wulkanowy.ui.base.BaseView - -interface LoginStudentSelectView : BaseView { - - fun initView() - - fun updateData(data: List) - - fun openMainView() - - fun showProgress(show: Boolean) - - fun showContent(show: Boolean) - - fun showActionBar(show: Boolean) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt deleted file mode 100644 index 87de0a818..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt +++ /dev/null @@ -1,116 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.symbol - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.view.ViewGroup -import android.view.inputmethod.EditorInfo.IME_ACTION_DONE -import android.view.inputmethod.EditorInfo.IME_NULL -import android.widget.ArrayAdapter -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.utils.hideSoftInput -import io.github.wulkanowy.utils.setOnTextChangedListener -import io.github.wulkanowy.utils.showSoftInput -import kotlinx.android.synthetic.main.fragment_login_symbol.* -import javax.inject.Inject - -class LoginSymbolFragment : BaseFragment(), LoginSymbolView { - - @Inject - lateinit var presenter: LoginSymbolPresenter - - companion object { - private const val SAVED_LOGIN_DATA = "LOGIN_DATA" - - fun newInstance() = LoginSymbolFragment() - } - - override val symbolNameError: CharSequence? - get() = loginSymbolNameLayout.error - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_login_symbol, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this, savedInstanceState?.getSerializable(SAVED_LOGIN_DATA)) - } - - override fun initView() { - loginSymbolSignIn.setOnClickListener { presenter.attemptLogin(loginSymbolName.text.toString()) } - - loginSymbolName.setOnTextChangedListener { presenter.onSymbolTextChanged() } - - loginSymbolName.apply { - setOnEditorActionListener { _, id, _ -> - if (id == IME_ACTION_DONE || id == IME_NULL) loginSymbolSignIn.callOnClick() else false - } - setAdapter(ArrayAdapter(context, android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values))) - } - } - - fun onParentInitSymbolFragment(loginData: Triple) { - presenter.onParentInitSymbolView(loginData) - } - - override fun setErrorSymbolIncorrect() { - loginSymbolNameLayout.apply { - requestFocus() - error = getString(R.string.login_incorrect_symbol) - } - } - - override fun setErrorSymbolRequire() { - loginSymbolNameLayout.apply { - requestFocus() - error = getString(R.string.login_field_required) - } - } - - override fun clearSymbolError() { - loginSymbolNameLayout.error = null - } - - override fun clearAndFocusSymbol() { - loginSymbolNameLayout.apply { - editText?.text = null - requestFocus() - } - } - - override fun showSoftKeyboard() { - activity?.showSoftInput() - } - - override fun hideSoftKeyboard() { - activity?.hideSoftInput() - } - - override fun showProgress(show: Boolean) { - loginSymbolProgress.visibility = if (show) VISIBLE else GONE - } - - override fun showContent(show: Boolean) { - loginSymbolContainer.visibility = if (show) VISIBLE else GONE - } - - override fun notifyParentAccountLogged(students: List) { - (activity as? LoginActivity)?.onSymbolFragmentAccountLogged(students) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putSerializable(SAVED_LOGIN_DATA, presenter.loginData) - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt deleted file mode 100644 index 0adb9e16f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt +++ /dev/null @@ -1,86 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.symbol - -import com.google.firebase.analytics.FirebaseAnalytics.Param.SUCCESS -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.reactivex.Single -import timber.log.Timber -import java.io.Serializable -import javax.inject.Inject - -class LoginSymbolPresenter @Inject constructor( - private val studentRepository: StudentRepository, - private val errorHandler: LoginErrorHandler, - private val schedulers: SchedulersProvider, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - var loginData: Triple? = null - - @Suppress("UNCHECKED_CAST") - fun onAttachView(view: LoginSymbolView, savedLoginData: Serializable?) { - super.onAttachView(view) - view.initView() - if (savedLoginData is Triple<*, *, *>) { - loginData = savedLoginData as Triple - } - } - - fun onSymbolTextChanged() { - view?.apply { if (symbolNameError != null) clearSymbolError() } - } - - fun attemptLogin(symbol: String) { - if (symbol.isBlank()) { - view?.setErrorSymbolRequire() - return - } - - disposable.add( - Single.fromCallable { if (loginData == null) throw IllegalArgumentException("Login data is null") else loginData } - .flatMap { studentRepository.getStudents(it.first, it.second, it.third, symbol) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doOnSubscribe { - view?.apply { - hideSoftKeyboard() - showProgress(true) - showContent(false) - } - Timber.i("Login with symbol started") - } - .doFinally { - view?.apply { - showProgress(false) - showContent(true) - } - } - .subscribe({ - analytics.logEvent("registration_symbol", SUCCESS to true, "students" to it.size, "endpoint" to loginData?.third, "symbol" to symbol, "error" to "No error") - view?.apply { - if (it.isEmpty()) { - Timber.i("Login with symbol result: Empty student list") - setErrorSymbolIncorrect() - } else { - Timber.i("Login with symbol result: Success") - notifyParentAccountLogged(it) - } - } - }, { - Timber.i("Login with symbol result: An exception occurred") - analytics.logEvent("registration_symbol", SUCCESS to false, "students" to -1, "endpoint" to loginData?.third, "symbol" to symbol, "error" to it.localizedMessage) - errorHandler.dispatch(it) - })) - } - - fun onParentInitSymbolView(loginData: Triple) { - this.loginData = loginData - view?.apply { - clearAndFocusSymbol() - showSoftKeyboard() - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt deleted file mode 100644 index 2e5143ef1..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.symbol - -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BaseView - -interface LoginSymbolView : BaseView { - - val symbolNameError: CharSequence? - - fun initView() - - fun setErrorSymbolIncorrect() - - fun setErrorSymbolRequire() - - fun clearSymbolError() - - fun clearAndFocusSymbol() - - fun showSoftKeyboard() - - fun hideSoftKeyboard() - - fun showProgress(show: Boolean) - - fun showContent(show: Boolean) - - fun notifyParentAccountLogged(students: List) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt deleted file mode 100644 index cff6175c0..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt +++ /dev/null @@ -1,71 +0,0 @@ -package io.github.wulkanowy.ui.modules.luckynumber - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainView -import kotlinx.android.synthetic.main.fragment_lucky_number.* -import javax.inject.Inject - -class LuckyNumberFragment : BaseSessionFragment(), LuckyNumberView, MainView.TitledView { - - @Inject - lateinit var presenter: LuckyNumberPresenter - - companion object { - fun newInstance() = LuckyNumberFragment() - } - - override val titleStringId: Int - get() = R.string.lucky_number_title - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_lucky_number, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this) - } - - override fun initView() { - luckyNumberSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun updateData(data: LuckyNumber) { - luckyNumberText.text = data.luckyNumber.toString() - } - - override fun hideRefresh() { - luckyNumberSwipe.isRefreshing = false - } - - override fun showEmpty(show: Boolean) { - luckyNumberEmpty.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showProgress(show: Boolean) { - luckyNumberProgress.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun enableSwipe(enable: Boolean) { - luckyNumberSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - luckyNumberContent.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun isViewEmpty(): Boolean { - return luckyNumberText.text.isBlank() - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt deleted file mode 100644 index 74ab0d1fe..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt +++ /dev/null @@ -1,76 +0,0 @@ -package io.github.wulkanowy.ui.modules.luckynumber - -import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import timber.log.Timber -import javax.inject.Inject - -class LuckyNumberPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val luckyNumberRepository: LuckyNumberRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: LuckyNumberView) { - super.onAttachView(view) - Timber.i("Lucky number view is attached") - view.run { - initView() - showContent(false) - enableSwipe(false) - } - loadData() - } - - private fun loadData(forceRefresh: Boolean = false) { - Timber.i("Loading lucky number started") - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMapMaybe { luckyNumberRepository.getLuckyNumber(it, forceRefresh) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - } - } - .subscribe({ - Timber.i("Loading lucky number result: Success") - view?.apply { - updateData(it) - showContent(true) - showEmpty(false) - } - analytics.logEvent("load_lucky_number", "lucky_number" to it.luckyNumber, "force_refresh" to forceRefresh) - }, { - Timber.i("Loading lucky number result: An exception occurred") - view?.run { showEmpty(isViewEmpty()) } - errorHandler.dispatch(it) - }, { - Timber.i("Loading lucky number result: No lucky number found") - view?.run { - showContent(false) - showEmpty(true) - } - }) - ) - } - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the lucky number") - loadData(true) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt deleted file mode 100644 index 5c19142c5..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.ui.modules.luckynumber - -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface LuckyNumberView : BaseSessionView { - - fun initView() - - fun updateData(data: LuckyNumber) - - fun hideRefresh() - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun isViewEmpty(): Boolean -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt deleted file mode 100644 index 4448e556b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt +++ /dev/null @@ -1,180 +0,0 @@ -package io.github.wulkanowy.ui.modules.main - -import android.content.Context -import android.content.Intent -import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK -import android.content.Intent.FLAG_ACTIVITY_NEW_TASK -import android.os.Bundle -import android.os.Handler -import android.view.Menu -import android.view.MenuItem -import androidx.appcompat.app.AlertDialog -import androidx.core.content.ContextCompat -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.Fragment -import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.TitleState.ALWAYS_SHOW -import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem -import com.ncapdevi.fragnav.FragNavController -import com.ncapdevi.fragnav.FragNavController.Companion.HIDE -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.BaseActivity -import io.github.wulkanowy.ui.modules.account.AccountDialog -import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment -import io.github.wulkanowy.ui.modules.exam.ExamFragment -import io.github.wulkanowy.ui.modules.grade.GradeFragment -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.more.MoreFragment -import io.github.wulkanowy.ui.modules.timetable.TimetableFragment -import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.safelyPopFragment -import io.github.wulkanowy.utils.setOnViewChangeListener -import kotlinx.android.synthetic.main.activity_main.* -import javax.inject.Inject - -class MainActivity : BaseActivity(), MainView { - - @Inject - lateinit var presenter: MainPresenter - - @Inject - lateinit var navController: FragNavController - - companion object { - const val EXTRA_START_MENU_INDEX = "extraStartMenuIndex" - - fun getStartIntent(context: Context) = Intent(context, MainActivity::class.java) - } - - override val isRootView: Boolean - get() = navController.isRootFragment - - override val currentViewTitle: String? - get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId?.let { getString(it) } - - override val currentStackSize: Int? - get() = navController.currentStack?.size - - override var startMenuIndex = 0 - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - setSupportActionBar(mainToolbar) - messageContainer = mainFragmentContainer - - presenter.onAttachView(this, intent.getIntExtra(EXTRA_START_MENU_INDEX, -1)) - navController.initialize(startMenuIndex, savedInstanceState) - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.action_menu_main, menu) - return true - } - - override fun initView() { - mainBottomNav.run { - addItems( - mutableListOf( - AHBottomNavigationItem(R.string.grade_title, R.drawable.ic_menu_main_grade_26dp, 0), - AHBottomNavigationItem(R.string.attendance_title, R.drawable.ic_menu_main_attendance_24dp, 0), - AHBottomNavigationItem(R.string.exam_title, R.drawable.ic_menu_main_exam_24dp, 0), - AHBottomNavigationItem(R.string.timetable_title, R.drawable.ic_menu_main_timetable_24dp, 0), - AHBottomNavigationItem(R.string.more_title, R.drawable.ic_menu_main_more_24dp, 0) - ) - ) - accentColor = ContextCompat.getColor(context, R.color.colorPrimary) - inactiveColor = getThemeAttrColor(android.R.attr.textColorSecondary) - defaultBackgroundColor = getThemeAttrColor(R.attr.bottomNavBackground) - titleState = ALWAYS_SHOW - currentItem = startMenuIndex - isBehaviorTranslationEnabled = false - setTitleTextSizeInSp(10f, 10f) - setOnTabSelectedListener { position, wasSelected -> - presenter.onTabSelected(position, wasSelected) - } - } - - navController.run { - setOnViewChangeListener { presenter.onViewChange() } - fragmentHideStrategy = HIDE - rootFragments = listOf( - GradeFragment.newInstance(), - AttendanceFragment.newInstance(), - ExamFragment.newInstance(), - TimetableFragment.newInstance(), - MoreFragment.newInstance() - ) - } - } - - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - return if (item?.itemId == R.id.mainMenuAccount) presenter.onAccountManagerSelected() - else false - } - - override fun onSupportNavigateUp(): Boolean { - return presenter.onUpNavigate() - } - - override fun switchMenuView(position: Int) { - navController.switchTab(position) - } - - override fun setViewTitle(title: String) { - supportActionBar?.title = title - } - - override fun showHomeArrow(show: Boolean) { - supportActionBar?.setDisplayHomeAsUpEnabled(show) - } - - override fun showAccountPicker() { - navController.showDialogFragment(AccountDialog.newInstance()) - } - - fun showExpiredDialog() { - AlertDialog.Builder(this) - .setTitle(R.string.main_session_expired) - .setMessage(R.string.main_session_relogin) - .setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onLoginSelected() } - .setNegativeButton(android.R.string.cancel) { _, _ -> } - .show() - } - - override fun notifyMenuViewReselected() { - Handler().postDelayed({ - (navController.currentStack?.get(0) as? MainView.MainChildView)?.onFragmentReselected() - }, 250) - } - - fun showDialogFragment(dialog: DialogFragment) { - navController.showDialogFragment(dialog) - } - - fun pushView(fragment: Fragment) { - navController.pushFragment(fragment) - } - - override fun popView() { - navController.safelyPopFragment() - } - - override fun onBackPressed() { - presenter.onBackPressed { super.onBackPressed() } - } - - override fun openLoginView() { - startActivity(LoginActivity.getStartIntent(this) - .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) }) - } - - override fun onSaveInstanceState(outState: Bundle?) { - super.onSaveInstanceState(outState) - navController.onSaveInstanceState(outState) - } - - override fun onDestroy() { - super.onDestroy() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt deleted file mode 100644 index de4405b74..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt +++ /dev/null @@ -1,100 +0,0 @@ -package io.github.wulkanowy.ui.modules.main - -import com.ncapdevi.fragnav.FragNavController -import dagger.Module -import dagger.Provides -import dagger.android.ContributesAndroidInjector -import io.github.wulkanowy.R -import io.github.wulkanowy.di.scopes.PerFragment -import io.github.wulkanowy.ui.modules.about.AboutFragment -import io.github.wulkanowy.ui.modules.about.AboutModule -import io.github.wulkanowy.ui.modules.account.AccountDialog -import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment -import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment -import io.github.wulkanowy.ui.modules.exam.ExamFragment -import io.github.wulkanowy.ui.modules.grade.GradeFragment -import io.github.wulkanowy.ui.modules.grade.GradeModule -import io.github.wulkanowy.ui.modules.homework.HomeworkFragment -import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment -import io.github.wulkanowy.ui.modules.message.MessageFragment -import io.github.wulkanowy.ui.modules.message.MessageModule -import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment -import io.github.wulkanowy.ui.modules.more.MoreFragment -import io.github.wulkanowy.ui.modules.note.NoteFragment -import io.github.wulkanowy.ui.modules.settings.SettingsFragment -import io.github.wulkanowy.ui.modules.timetable.TimetableFragment -import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment - -@Module -abstract class MainModule { - - @Module - companion object { - - @JvmStatic - @Provides - fun provideFragNavController(activity: MainActivity): FragNavController { - return FragNavController(activity.supportFragmentManager, R.id.mainFragmentContainer) - } - } - - @PerFragment - @ContributesAndroidInjector - abstract fun bindAttendanceFragment(): AttendanceFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindAttendanceSummaryFragment(): AttendanceSummaryFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindExamFragment(): ExamFragment - - @PerFragment - @ContributesAndroidInjector(modules = [GradeModule::class]) - abstract fun bindGradeFragment(): GradeFragment - - @PerFragment - @ContributesAndroidInjector(modules = [MessageModule::class]) - abstract fun bindMessagesFragment(): MessageFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindMessagePreviewFragment(): MessagePreviewFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindMoreFragment(): MoreFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindTimetableFragment(): TimetableFragment - - @PerFragment - @ContributesAndroidInjector(modules = [AboutModule::class]) - abstract fun bindAboutFragment(): AboutFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindSettingsFragment(): SettingsFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindNoteFragment(): NoteFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindHomeworkFragment(): HomeworkFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindLuckyNumberFragment(): LuckyNumberFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindCompletedLessonsFragment(): CompletedLessonsFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindAccountDialog(): AccountDialog -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt deleted file mode 100644 index 30f3b920f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt +++ /dev/null @@ -1,107 +0,0 @@ -package io.github.wulkanowy.ui.modules.main - -import com.google.firebase.analytics.FirebaseAnalytics.Event.APP_OPEN -import com.google.firebase.analytics.FirebaseAnalytics.Param.DESTINATION -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.services.sync.SyncManager -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.reactivex.Completable -import timber.log.Timber -import javax.inject.Inject - -class MainPresenter @Inject constructor( - private val errorHandler: ErrorHandler, - private val studentRepository: StudentRepository, - private val prefRepository: PreferencesRepository, - private val syncManager: SyncManager, - private val schedulers: SchedulersProvider, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - fun onAttachView(view: MainView, initMenuIndex: Int) { - super.onAttachView(view) - view.run { - startMenuIndex = if (initMenuIndex != -1) initMenuIndex else prefRepository.startMenuIndex - Timber.i("Main view is attached with $startMenuIndex menu index") - initView() - } - - syncManager.startSyncWorker() - - analytics.logEvent(APP_OPEN, DESTINATION to when (initMenuIndex) { - 1 -> "Grades" - 3 -> "Timetable" - 4 -> "More" - else -> "User action" - }) - } - - fun onViewChange() { - view?.apply { - currentViewTitle?.let { setViewTitle(it) } - currentStackSize?.let { - if (it > 1) showHomeArrow(true) - else showHomeArrow(false) - } - } - } - - fun onAccountManagerSelected(): Boolean { - Timber.i("Select account manager") - view?.showAccountPicker() - return true - } - - fun onUpNavigate(): Boolean { - Timber.i("Up navigate pressed") - view?.popView() - return true - } - - fun onBackPressed(default: () -> Unit) { - Timber.i("Back pressed in main view") - view?.run { - if (isRootView) default() - else popView() - } - } - - fun onTabSelected(index: Int, wasSelected: Boolean): Boolean { - return view?.run { - Timber.i("Switch main tab index: $index, reselected: $wasSelected") - if (wasSelected) { - notifyMenuViewReselected() - false - } else { - switchMenuView(index) - true - } - } == true - } - - fun onLoginSelected() { - Timber.i("Attempt to switch the student after the session expires") - disposable.add(studentRepository.getCurrentStudent(false) - .flatMapCompletable { studentRepository.logoutStudent(it) } - .andThen(studentRepository.getSavedStudents(false)) - .flatMapCompletable { - if (it.isNotEmpty()) { - Timber.i("Switching current student") - studentRepository.switchStudent(it[0]) - } else Completable.complete() - } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ - Timber.i("Switch student result: Open login view") - view?.openLoginView() - }, { - Timber.i("Switch student result: An exception occurred") - errorHandler.dispatch(it) - })) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt deleted file mode 100644 index d3d7e342f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.ui.modules.main - -import io.github.wulkanowy.ui.base.BaseView - -interface MainView : BaseView { - - var startMenuIndex: Int - - val isRootView: Boolean - - val currentViewTitle: String? - - val currentStackSize: Int? - - fun initView() - - fun switchMenuView(position: Int) - - fun showHomeArrow(show: Boolean) - - fun showAccountPicker() - - fun notifyMenuViewReselected() - - fun setViewTitle(title: String) - - fun popView() - - fun openLoginView() - - interface MainChildView { - - fun onFragmentReselected() - } - - interface TitledView { - - val titleStringId: Int - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt deleted file mode 100644 index d8a6bc84c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt +++ /dev/null @@ -1,94 +0,0 @@ -package io.github.wulkanowy.ui.modules.message - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED -import io.github.wulkanowy.data.repositories.message.MessageFolder.SENT -import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity -import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment -import io.github.wulkanowy.utils.setOnSelectPageListener -import kotlinx.android.synthetic.main.fragment_message.* -import javax.inject.Inject - -class MessageFragment : BaseFragment(), MessageView, MainView.TitledView { - - @Inject - lateinit var presenter: MessagePresenter - - @Inject - lateinit var pagerAdapter: BaseFragmentPagerAdapter - - companion object { - fun newInstance() = MessageFragment() - } - - override val titleStringId: Int - get() = R.string.message_title - - override val currentPageIndex: Int - get() = messageViewPager.currentItem - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_message, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this) - } - - override fun initView() { - pagerAdapter.apply { - containerId = messageViewPager.id - addFragmentsWithTitle(mapOf( - MessageTabFragment.newInstance(RECEIVED) to getString(R.string.message_inbox), - MessageTabFragment.newInstance(SENT) to getString(R.string.message_sent), - MessageTabFragment.newInstance(TRASHED) to getString(R.string.message_trash) - )) - } - - messageViewPager.run { - adapter = pagerAdapter - offscreenPageLimit = 2 - setOnSelectPageListener { presenter.onPageSelected(it) } - } - messageTabLayout.setupWithViewPager(messageViewPager) - - openSendMessageButton.setOnClickListener { presenter.onSendMessageButtonClicked() } - } - - override fun showContent(show: Boolean) { - messageViewPager.visibility = if (show) VISIBLE else INVISIBLE - messageTabLayout.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showProgress(show: Boolean) { - messageProgress.visibility = if (show) VISIBLE else INVISIBLE - } - - fun onChildFragmentLoaded() { - presenter.onChildViewLoaded() - } - - override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) { - (pagerAdapter.getFragmentInstance(index) as? MessageView.MessageChildView)?.onParentLoadData(forceRefresh) - } - - override fun openSendMessage() { - context?.let { it.startActivity(SendMessageActivity.getStartIntent(it)) } - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt deleted file mode 100644 index 26568e22f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt +++ /dev/null @@ -1,62 +0,0 @@ -package io.github.wulkanowy.ui.modules.message - -import android.graphics.Typeface.BOLD -import android.graphics.Typeface.NORMAL -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_message.* - -class MessageItem(val message: Message, private val noSubjectString: String) : - AbstractFlexibleItem() { - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun getLayoutRes() = R.layout.item_message - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - val style = if (message.unread) BOLD else NORMAL - - messageItemAuthor.run { - text = if (message.recipient.isNotBlank()) message.recipient else message.sender - setTypeface(null, style) - } - messageItemSubject.run { - text = if (message.subject.isNotBlank()) message.subject else noSubjectString - setTypeface(null, style) - } - messageItemDate.run { - text = message.date.toFormattedString() - setTypeface(null, style) - } - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as MessageItem - - if (message != other.message) return false - return true - } - - override fun hashCode(): Int { - return message.hashCode() - } - - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageModule.kt deleted file mode 100644 index e4c202d9f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageModule.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.ui.modules.message - -import dagger.Module -import dagger.Provides -import dagger.android.ContributesAndroidInjector -import io.github.wulkanowy.di.scopes.PerChildFragment -import io.github.wulkanowy.di.scopes.PerFragment -import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment - -@Module -abstract class MessageModule { - - @Module - companion object { - - @JvmStatic - @PerFragment - @Provides - fun provideMessageAdapter(fragment: MessageFragment) = BaseFragmentPagerAdapter(fragment.childFragmentManager) - } - - @PerChildFragment - @ContributesAndroidInjector - abstract fun bindMessageTabFragment(): MessageTabFragment -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt deleted file mode 100644 index 332d5b74f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt +++ /dev/null @@ -1,49 +0,0 @@ -package io.github.wulkanowy.ui.modules.message - -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.SchedulersProvider -import io.reactivex.Completable -import timber.log.Timber -import java.util.concurrent.TimeUnit.MILLISECONDS -import javax.inject.Inject - -class MessagePresenter @Inject constructor( - errorHandler: ErrorHandler, - private val schedulers: SchedulersProvider -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: MessageView) { - super.onAttachView(view) - Timber.i("Message view is attached") - disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread) - .subscribe { - view.initView() - loadData() - }) - } - - fun onPageSelected(index: Int) { - loadChild(index) - } - - private fun loadData() { - view?.run { loadChild(currentPageIndex) } - } - - private fun loadChild(index: Int, forceRefresh: Boolean = false) { - Timber.i("Load message child view index: $index") - view?.notifyChildLoadData(index, forceRefresh) - } - - fun onChildViewLoaded() { - view?.apply { - showContent(true) - showProgress(false) - } - } - - fun onSendMessageButtonClicked() { - view?.openSendMessage() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt deleted file mode 100644 index 41257ecc7..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.ui.modules.message - -import io.github.wulkanowy.ui.base.BaseView - -interface MessageView : BaseView { - - val currentPageIndex: Int - - fun initView() - - fun showContent(show: Boolean) - - fun showProgress(show: Boolean) - - fun notifyChildLoadData(index: Int, forceRefresh: Boolean) - - fun openSendMessage() - - interface MessageChildView { - - fun onParentLoadData(forceRefresh: Boolean) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt deleted file mode 100644 index 102567eda..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ /dev/null @@ -1,116 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.preview - -import android.annotation.SuppressLint -import android.os.Bundle -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.view.ViewGroup -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity -import kotlinx.android.synthetic.main.fragment_message_preview.* -import javax.inject.Inject - -@SuppressLint("SetTextI18n") -class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainView.TitledView { - - @Inject - lateinit var presenter: MessagePreviewPresenter - - private var menuReplyButton: MenuItem? = null - - override val titleStringId: Int - get() = R.string.message_title - - override val noSubjectString: String - get() = getString(R.string.message_no_subject) - - companion object { - const val MESSAGE_ID_KEY = "message_id" - - fun newInstance(messageId: Int?): MessagePreviewFragment { - return MessagePreviewFragment().apply { - arguments = Bundle().apply { putInt(MESSAGE_ID_KEY, messageId ?: 0) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_message_preview, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = messagePreviewContainer - presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getInt(MESSAGE_ID_KEY) ?: 0) - } - - override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { - inflater?.inflate(R.menu.action_menu_message_preview, menu) - menuReplyButton = menu?.findItem(R.id.messagePreviewMenuReply) - presenter.onCreateOptionsMenu() - } - - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - return if (item?.itemId == R.id.messagePreviewMenuReply) presenter.onReply() - else false - } - - override fun setSubject(subject: String) { - messagePreviewSubject.text = subject - } - - override fun setRecipient(recipient: String) { - messagePreviewAuthor.text = "${getString(R.string.message_to)} $recipient" - } - - override fun setSender(sender: String) { - messagePreviewAuthor.text = "${getString(R.string.message_from)} $sender" - } - - override fun setDate(date: String) { - messagePreviewDate.text = getString(R.string.message_date, date) - } - - override fun setContent(content: String) { - messagePreviewContent.text = content - } - - override fun showProgress(show: Boolean) { - messagePreviewProgress.visibility = if (show) VISIBLE else GONE - } - - override fun showReplyButton(show: Boolean) { - menuReplyButton?.isVisible = show - } - - override fun showMessageError() { - messagePreviewError.visibility = VISIBLE - } - - override fun openMessageReply(message: Message?) { - context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message)) } - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putInt(MESSAGE_ID_KEY, presenter.messageId) - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt deleted file mode 100644 index a5ed9f28c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ /dev/null @@ -1,75 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.preview - -import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.repositories.message.MessageRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.toFormattedString -import timber.log.Timber -import javax.inject.Inject - -class MessagePreviewPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val messageRepository: MessageRepository, - private val studentRepository: StudentRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - var messageId: Int = 0 - - private var replyMessage: Message? = null - - fun onAttachView(view: MessagePreviewView, id: Int) { - super.onAttachView(view) - loadData(id) - } - - private fun loadData(id: Int) { - Timber.i("Loading message $id preview started") - messageId = id - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .flatMap { messageRepository.getMessage(it, messageId, true) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { view?.showProgress(false) } - .subscribe({ message -> - Timber.i("Loading message $id preview result: Success ") - replyMessage = message - view?.run { - message.let { - setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString) - setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) - setContent(it.content.orEmpty()) - showReplyButton(true) - - if (it.recipient.isNotBlank()) setRecipient(it.recipient) - else setSender(it.sender) - } - } - analytics.logEvent("load_message_preview", START_DATE to message.date.toFormattedString("yyyy.MM.dd"), "length" to message.content?.length) - }) { - Timber.i("Loading message $id preview result: An exception occurred ") - view?.showMessageError() - errorHandler.dispatch(it) - }) - } - } - - fun onReply(): Boolean { - return if (replyMessage != null) { - view?.openMessageReply(replyMessage) - true - } else false - } - - fun onCreateOptionsMenu() { - view?.showReplyButton(replyMessage != null) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt deleted file mode 100644 index 464ea168c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.preview - -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface MessagePreviewView : BaseSessionView { - - val noSubjectString: String - - fun setSubject(subject: String) - - fun setRecipient(recipient: String) - - fun setSender(sender: String) - - fun setDate(date: String) - - fun setContent(content: String) - - fun showProgress(show: Boolean) - - fun showReplyButton(show: Boolean) - - fun showMessageError() - - fun openMessageReply(message: Message?) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChip.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChip.kt deleted file mode 100644 index be433b008..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChip.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.send - -import android.graphics.drawable.Drawable -import android.net.Uri -import com.pchmn.materialchips.model.ChipInterface -import io.github.wulkanowy.data.db.entities.Recipient - -class RecipientChip(var recipient: Recipient) : ChipInterface { - - override fun getAvatarDrawable(): Drawable? = null - - override fun getAvatarUri(): Uri? = null - - override fun getId(): Any = recipient.id - - override fun getLabel(): String = recipient.name - - override fun getInfo(): String { - return recipient.realName.run { - substringBeforeLast("-").let { sub -> - when { - (sub == this) -> this - (sub.indexOf('(') != -1) -> indexOf("(").let { substring(if (it != -1) it else 0) } - (sub.indexOf('[') != -1) -> indexOf("[").let { substring(if (it != -1) it else 0) } - else -> substringAfter('-') - } - }.trim() - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt deleted file mode 100644 index edf14dd64..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt +++ /dev/null @@ -1,131 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.send - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.view.Menu -import android.view.MenuItem -import android.view.View.GONE -import android.view.View.VISIBLE -import android.widget.Toast -import android.widget.Toast.LENGTH_LONG -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.ui.base.BaseActivity -import io.github.wulkanowy.utils.hideSoftInput -import io.github.wulkanowy.utils.showSoftInput -import kotlinx.android.synthetic.main.activity_send_message.* -import javax.inject.Inject - -class SendMessageActivity : BaseActivity(), SendMessageView { - - @Inject - lateinit var presenter: SendMessagePresenter - - companion object { - private const val EXTRA_MESSAGE = "EXTRA_MESSAGE" - - fun getStartIntent(context: Context) = Intent(context, SendMessageActivity::class.java) - - fun getStartIntent(context: Context, message: Message?): Intent { - return getStartIntent(context).putExtra(EXTRA_MESSAGE, message) - } - } - - override val formRecipientsData: List - get() = (sendMessageRecipientsInput.selectedChipList).map { (it as RecipientChip).recipient } - - override val formSubjectValue: String - get() = sendMessageSubjectInput.text.toString() - - override val formContentValue: String - get() = sendMessageContentInput.text.toString() - - override val messageRequiredRecipients: String - get() = getString(R.string.message_required_recipients) - - override val messageContentMinLength: String - get() = getString(R.string.message_content_min_length) - - override val messageSuccess: String - get() = getString(R.string.message_send_successful) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_send_message) - setSupportActionBar(sendMessageToolbar) - supportActionBar?.setDisplayHomeAsUpEnabled(true) - messageContainer = sendMessageContainer - - presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message) - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.action_menu_send_message, menu) - return true - } - - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - return if (item?.itemId == R.id.sendMessageMenuSend) presenter.onSend() - else false - } - - override fun onSupportNavigateUp(): Boolean { - return presenter.onUpNavigate() - } - - override fun setReportingUnit(unit: ReportingUnit) { - sendMessageFromTextView.setText(unit.senderName) - } - - override fun setRecipients(recipients: List) { - sendMessageRecipientsInput.filterableList = recipients.map { RecipientChip(it) } - } - - override fun setSelectedRecipients(recipients: List) { - recipients.map { sendMessageRecipientsInput.addChip(RecipientChip(it)) } - } - - override fun showProgress(show: Boolean) { - sendMessageProgress.visibility = if (show) VISIBLE else GONE - } - - override fun showContent(show: Boolean) { - sendMessageContent.visibility = if (show) VISIBLE else GONE - } - - override fun showEmpty(show: Boolean) { - sendMessageEmpty.visibility = if (show) VISIBLE else GONE - } - - override fun showActionBar(show: Boolean) { - supportActionBar?.apply { if (show) show() else hide() } - } - - override fun setSubject(subject: String) { - sendMessageSubjectInput.setText(subject) - } - - override fun setContent(content: String) { - sendMessageContentInput.setText(content) - } - - override fun showMessage(text: String) { - Toast.makeText(this, text, LENGTH_LONG).show() - } - - override fun showSoftInput(show: Boolean) { - if (show) showSoftInput() else hideSoftInput() - } - - override fun popView() { - onBackPressed() - } - - override fun onDestroy() { - presenter.onDetachView() - super.onDestroy() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt deleted file mode 100644 index a3715aaa6..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt +++ /dev/null @@ -1,158 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.send - -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.repositories.message.MessageRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.recipient.RecipientRepository -import io.github.wulkanowy.data.repositories.reportingunit.ReportingUnitRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.toFormattedString -import io.reactivex.Completable -import timber.log.Timber -import javax.inject.Inject - -class SendMessagePresenter @Inject constructor( - private val errorHandler: ErrorHandler, - private val schedulers: SchedulersProvider, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val messageRepository: MessageRepository, - private val reportingUnitRepository: ReportingUnitRepository, - private val recipientRepository: RecipientRepository, - private val preferencesRepository: PreferencesRepository, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - fun onAttachView(view: SendMessageView, message: Message?) { - super.onAttachView(view) - Timber.i("Send message view is attached") - loadData(message) - view.apply { - message?.let { - setSubject("RE: ${message.subject}") - if (preferencesRepository.fillMessageContent) { - setContent(when (message.sender.isNotEmpty()) { - true -> "\n\nOd: ${message.sender}\n" - false -> "\n\nDo: ${message.recipient}\n" - } + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}") - } - } - } - } - - fun onUpNavigate(): Boolean { - view?.popView() - return true - } - - private fun loadData(message: Message?) { - var reportingUnit: ReportingUnit? = null - var recipients: List = emptyList() - var selectedRecipient: List = emptyList() - - Timber.i("Loading recipients started") - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it).map { semester -> it to semester } } - .flatMapCompletable { (student, semester) -> - reportingUnitRepository.getReportingUnit(student, semester.unitId) - .doOnSuccess { reportingUnit = it } - .flatMap { recipientRepository.getRecipients(student, 2, it).toMaybe() } - .doOnSuccess { - Timber.i("Loading recipients result: Success, fetched %d recipients", it.size) - recipients = it - } - .flatMapCompletable { - if (message == null) Completable.complete() - else recipientRepository.getMessageRecipients(student, message) - .doOnSuccess { - Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", it.size) - selectedRecipient = it - } - .ignoreElement() - } - } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doOnSubscribe { - view?.run { - showProgress(true) - showContent(false) - } - } - .doFinally { view?.run { showProgress(false) } } - .subscribe({ - view?.apply { - if (reportingUnit !== null) { - reportingUnit?.let { setReportingUnit(it) } - setRecipients(recipients) - if (selectedRecipient.isNotEmpty()) setSelectedRecipients(selectedRecipient) - showContent(true) - } else { - Timber.e("Loading recipients result: Can't find the reporting unit") - view?.showEmpty(true) - } - } - }, { - Timber.e("Loading recipients result: An exception occurred") - view?.showContent(true) - errorHandler.dispatch(it) - })) - } - - private fun sendMessage(subject: String, content: String, recipients: List) { - Timber.i("Sending message started") - disposable.add(messageRepository.sendMessage(subject, content, recipients) - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doOnSubscribe { - view?.run { - showSoftInput(false) - showContent(false) - showProgress(true) - showActionBar(false) - } - } - .subscribe({ - Timber.i("Sending message result: Success") - analytics.logEvent("send_message", "recipients" to recipients.size) - view?.run { - showMessage(messageSuccess) - popView() - } - }, { - Timber.i("Sending message result: An exception occurred") - view?.run { - showContent(true) - showProgress(false) - showActionBar(true) - } - errorHandler.dispatch(it) - }) - ) - } - - fun onSend(): Boolean { - view?.run { - when { - formRecipientsData.isEmpty() -> showMessage(messageRequiredRecipients) - formContentValue.length < 3 -> showMessage(messageContentMinLength) - else -> { - sendMessage( - subject = formSubjectValue, - content = formContentValue, - recipients = formRecipientsData - ) - return true - } - } - } - return false - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt deleted file mode 100644 index 8a2cb4f61..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.send - -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.ui.base.BaseView - -interface SendMessageView : BaseView { - - val formRecipientsData: List - - val formSubjectValue: String - - val formContentValue: String - - val messageRequiredRecipients: String - - val messageContentMinLength: String - - val messageSuccess: String - - fun setReportingUnit(unit: ReportingUnit) - - fun setRecipients(recipients: List) - - fun setSelectedRecipients(recipients: List) - - fun showProgress(show: Boolean) - - fun showContent(show: Boolean) - - fun showEmpty(show: Boolean) - - fun showActionBar(show: Boolean) - - fun setSubject(subject: String) - - fun setContent(content: String) - - fun showSoftInput(show: Boolean) - - fun popView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt deleted file mode 100644 index 047f2a34c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt +++ /dev/null @@ -1,131 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.tab - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.message.MessageFolder -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.message.MessageFragment -import io.github.wulkanowy.ui.modules.message.MessageItem -import io.github.wulkanowy.ui.modules.message.MessageView -import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_message_tab.* -import javax.inject.Inject - -class MessageTabFragment : BaseSessionFragment(), MessageTabView, MessageView.MessageChildView { - - @Inject - lateinit var presenter: MessageTabPresenter - - @Inject - lateinit var tabAdapter: FlexibleAdapter> - - companion object { - const val MESSAGE_TAB_FOLDER_ID = "message_tab_folder_id" - - fun newInstance(folder: MessageFolder): MessageTabFragment { - return MessageTabFragment().apply { - arguments = Bundle().apply { - putString(MESSAGE_TAB_FOLDER_ID, folder.name) - } - } - } - } - - override val noSubjectString: String - get() = getString(R.string.message_no_subject) - - override val isViewEmpty - get() = tabAdapter.isEmpty - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_message_tab, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = messageTabRecycler - presenter.onAttachView(this, MessageFolder.valueOf( - (savedInstanceState ?: arguments)?.getString(MessageTabFragment.MESSAGE_TAB_FOLDER_ID).orEmpty() - )) - } - - override fun initView() { - tabAdapter.setOnItemClickListener { presenter.onMessageItemSelected(it) } - - messageTabRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = tabAdapter - addItemDecoration(FlexibleItemDecoration(context) - .withDefaultDivider() - .withDrawDividerOnLastItem(false) - ) - } - messageTabSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun updateData(data: List) { - tabAdapter.updateDataSet(data, true) - } - - override fun updateItem(item: AbstractFlexibleItem<*>) { - tabAdapter.updateItem(item) - } - - override fun clearView() { - tabAdapter.clear() - } - - override fun showProgress(show: Boolean) { - messageTabProgress.visibility = if (show) VISIBLE else GONE - } - - override fun enableSwipe(enable: Boolean) { - messageTabSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - messageTabRecycler.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showEmpty(show: Boolean) { - messageTabEmpty.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showRefresh(show: Boolean) { - messageTabSwipe.isRefreshing = show - } - - override fun openMessage(messageId: Int?) { - (activity as? MainActivity)?.pushView(MessagePreviewFragment.newInstance(messageId)) - } - - override fun notifyParentDataLoaded() { - (parentFragment as? MessageFragment)?.onChildFragmentLoaded() - } - - override fun onParentLoadData(forceRefresh: Boolean) { - presenter.onParentViewLoadData(forceRefresh) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putString(MessageTabFragment.MESSAGE_TAB_FOLDER_ID, presenter.folder.name) - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt deleted file mode 100644 index 7d9fda0d3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ /dev/null @@ -1,95 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.tab - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.repositories.message.MessageFolder -import io.github.wulkanowy.data.repositories.message.MessageRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.ui.modules.message.MessageItem -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import timber.log.Timber -import javax.inject.Inject - -class MessageTabPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val messageRepository: MessageRepository, - private val studentRepository: StudentRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - lateinit var folder: MessageFolder - - fun onAttachView(view: MessageTabView, folder: MessageFolder) { - super.onAttachView(view) - view.initView() - this.folder = folder - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the $folder message") - onParentViewLoadData(true) - } - - fun onParentViewLoadData(forceRefresh: Boolean) { - Timber.i("Loading $folder message data started") - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .flatMap { messageRepository.getMessages(it, folder, forceRefresh) } - .map { items -> items.map { MessageItem(it, view?.noSubjectString.orEmpty()) } } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - showRefresh(false) - showProgress(false) - enableSwipe(true) - notifyParentDataLoaded() - } - } - .subscribe({ - Timber.i("Loading $folder message result: Success") - view?.run { - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - updateData(it) - } - analytics.logEvent("load_messages", "items" to it.size, "folder" to folder.name) - }) { - Timber.i("Loading $folder message result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } - } - - fun onMessageItemSelected(item: AbstractFlexibleItem<*>) { - if (item is MessageItem) { - Timber.i("Select message ${item.message.realId} item") - view?.run { - openMessage(item.message.realId) - if (item.message.unread) { - item.message.unread = false - updateItem(item) - updateMessage(item.message) - } - } - } - } - - private fun updateMessage(message: Message) { - Timber.i("Attempt to update message ${message.realId}") - disposable.add(messageRepository.updateMessage(message) - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ Timber.d("Update message ${message.realId} result: Success") }) - { error -> - Timber.i("Update message ${message.realId} result: An exception occurred") - errorHandler.dispatch(error) - }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt deleted file mode 100644 index bcda52b56..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.tab - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.ui.base.session.BaseSessionView -import io.github.wulkanowy.ui.modules.message.MessageItem - -interface MessageTabView : BaseSessionView { - - val noSubjectString: String - - val isViewEmpty: Boolean - - fun initView() - - fun updateData(data: List) - - fun updateItem(item: AbstractFlexibleItem<*>) - - fun clearView() - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun showEmpty(show: Boolean) - - fun showRefresh(show: Boolean) - - fun openMessage(messageId: Int?) - - fun notifyParentDataLoaded() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt deleted file mode 100644 index 5bbf6247a..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt +++ /dev/null @@ -1,146 +0,0 @@ -package io.github.wulkanowy.ui.modules.more - -import android.graphics.drawable.Drawable -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.content.ContextCompat -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.about.AboutFragment -import io.github.wulkanowy.ui.modules.homework.HomeworkFragment -import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.ui.modules.message.MessageFragment -import io.github.wulkanowy.ui.modules.note.NoteFragment -import io.github.wulkanowy.ui.modules.settings.SettingsFragment -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_more.* -import javax.inject.Inject - -class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.MainChildView { - - @Inject - lateinit var presenter: MorePresenter - - @Inject - lateinit var moreAdapter: FlexibleAdapter> - - companion object { - fun newInstance() = MoreFragment() - } - - override val titleStringId: Int - get() = R.string.more_title - - - override val messagesRes: Pair? - get() { - return context?.run { - getString(R.string.message_title) to - ContextCompat.getDrawable(this, R.drawable.ic_more_messages_24dp) - } - } - - override val homeworkRes: Pair? - get() { - return context?.run { - getString(R.string.homework_title) to ContextCompat.getDrawable(this, R.drawable.ic_menu_main_homework_24dp) - } - } - - override val noteRes: Pair? - get() { - return context?.run { - getString(R.string.note_title) to ContextCompat.getDrawable(this, R.drawable.ic_menu_main_note_24dp) - } - } - - override val luckyNumberRes: Pair? - get() { - return context?.run { - getString(R.string.lucky_number_title) to - ContextCompat.getDrawable(this, R.drawable.ic_more_lucky_number_24dp) - } - } - - override val settingsRes: Pair? - get() { - return context?.run { - getString(R.string.settings_title) to - ContextCompat.getDrawable(this, R.drawable.ic_more_settings_24dp) - } - } - - override val aboutRes: Pair? - get() { - return context?.run { - getString(R.string.about_title) to - ContextCompat.getDrawable(this, R.drawable.ic_all_about_24dp) - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_more, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this) - } - - override fun initView() { - moreAdapter.run { setOnItemClickListener { presenter.onItemSelected(it) } } - - moreRecycler.apply { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = moreAdapter - } - } - - override fun onFragmentReselected() { - presenter.onViewReselected() - } - - override fun updateData(data: List) { - moreAdapter.updateDataSet(data) - } - - override fun openMessagesView() { - (activity as? MainActivity)?.pushView(MessageFragment.newInstance()) - } - - override fun openHomeworkView() { - (activity as? MainActivity)?.pushView(HomeworkFragment.newInstance()) - } - - override fun openNoteView() { - (activity as? MainActivity)?.pushView(NoteFragment.newInstance()) - } - - override fun openLuckyNumberView() { - (activity as? MainActivity)?.pushView(LuckyNumberFragment.newInstance()) - } - - override fun openSettingsView() { - (activity as? MainActivity)?.pushView(SettingsFragment.newInstance()) - } - - override fun openAboutView() { - (activity as? MainActivity)?.pushView(AboutFragment.newInstance()) - } - - override fun popView() { - (activity as? MainActivity)?.popView() - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt deleted file mode 100644 index 85b604e77..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.wulkanowy.ui.modules.more - -import android.graphics.drawable.Drawable -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_more.* - -class MoreItem(val title: String, private val drawable: Drawable?) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_more - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - moreItemTitle.text = title - moreItemImage.setImageDrawable(drawable) - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as MoreItem - - if (title != other.title) return false - - return true - } - - override fun hashCode(): Int { - return title.hashCode() - } - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt deleted file mode 100644 index 5fea241c7..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.ui.modules.more - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import timber.log.Timber -import javax.inject.Inject - -class MorePresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresenter(errorHandler) { - - override fun onAttachView(view: MoreView) { - super.onAttachView(view) - Timber.i("More view is attached") - view.initView() - loadData() - } - - fun onItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is MoreItem) { - Timber.i("Select more item \"${item.title}\"") - view?.run { - when (item.title) { - messagesRes?.first -> openMessagesView() - homeworkRes?.first -> openHomeworkView() - noteRes?.first -> openNoteView() - luckyNumberRes?.first -> openLuckyNumberView() - settingsRes?.first -> openSettingsView() - aboutRes?.first -> openAboutView() - } - } - } - } - - fun onViewReselected() { - Timber.i("More view is reselected") - view?.popView() - } - - private fun loadData() { - Timber.i("Load items for more view") - view?.run { - updateData(listOfNotNull( - messagesRes?.let { MoreItem(it.first, it.second) }, - homeworkRes?.let { MoreItem(it.first, it.second) }, - noteRes?.let { MoreItem(it.first, it.second) }, - luckyNumberRes?.let { MoreItem(it.first, it.second) }, - settingsRes?.let { MoreItem(it.first, it.second) }, - aboutRes?.let { MoreItem(it.first, it.second) }) - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt deleted file mode 100644 index 333edc5df..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.ui.modules.more - -import android.graphics.drawable.Drawable -import io.github.wulkanowy.ui.base.BaseView - -interface MoreView : BaseView { - - val messagesRes: Pair? - - val homeworkRes: Pair? - - val noteRes: Pair? - - val luckyNumberRes: Pair? - - val settingsRes: Pair? - - val aboutRes: Pair? - - fun initView() - - fun updateData(data: List) - - fun openSettingsView() - - fun openAboutView() - - fun popView() - - fun openMessagesView() - - fun openHomeworkView() - - fun openNoteView() - - fun openLuckyNumberView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt deleted file mode 100644 index 492aeab26..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.ui.modules.note - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_note.* - -class NoteDialog : DialogFragment() { - - private lateinit var note: Note - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(exam: Note): NoteDialog { - return NoteDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - note = getSerializable(ARGUMENT_KEY) as Note - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_note, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - noteDialogDate.text = note.date.toFormattedString() - noteDialogCategory.text = note.category - noteDialogTeacher.text = note.teacher - noteDialogContent.text = note.content - noteDialogClose.setOnClickListener { dismiss() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt deleted file mode 100644 index fc773e10b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt +++ /dev/null @@ -1,105 +0,0 @@ -package io.github.wulkanowy.ui.modules.note - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_note.* -import javax.inject.Inject - -class NoteFragment : BaseSessionFragment(), NoteView, MainView.TitledView { - - @Inject - lateinit var presenter: NotePresenter - - @Inject - lateinit var noteAdapter: FlexibleAdapter> - - companion object { - fun newInstance() = NoteFragment() - } - - override val titleStringId: Int - get() = R.string.note_title - - override val isViewEmpty: Boolean - get() = noteAdapter.isEmpty - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_note, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this) - } - - override fun initView() { - noteAdapter.run { - setOnItemClickListener { presenter.onNoteItemSelected(it) } - } - - noteRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = noteAdapter - addItemDecoration(FlexibleItemDecoration(context) - .withDefaultDivider() - .withDrawDividerOnLastItem(false) - ) - } - noteSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun showNoteDialog(note: Note) { - (activity as? MainActivity)?.showDialogFragment(NoteDialog.newInstance(note)) - } - - override fun updateData(data: List) { - noteAdapter.updateDataSet(data, true) - } - - override fun updateItem(item: AbstractFlexibleItem<*>) { - noteAdapter.updateItem(item) - } - - override fun clearData() { - noteAdapter.clear() - } - - override fun showEmpty(show: Boolean) { - noteEmpty.visibility = if (show) VISIBLE else GONE - } - - override fun showProgress(show: Boolean) { - noteProgress.visibility = if (show) VISIBLE else GONE - } - - override fun enableSwipe(enable: Boolean) { - noteSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - noteRecycler.visibility = if (show) VISIBLE else GONE - } - - override fun hideRefresh() { - noteSwipe.isRefreshing = false - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt deleted file mode 100644 index dabeef74a..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.ui.modules.note - -import android.graphics.Typeface.BOLD -import android.graphics.Typeface.NORMAL -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_note.* - -class NoteItem(val note: Note) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_note - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): NoteItem.ViewHolder { - return NoteItem.ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: NoteItem.ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - noteItemDate.apply { - text = note.date.toFormattedString() - setTypeface(null, if (note.isRead) NORMAL else BOLD) - } - noteItemType.apply { - text = note.category - setTypeface(null, if (note.isRead) NORMAL else BOLD) - } - noteItemTeacher.text = note.teacher - noteItemContent.text = note.content - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as NoteItem - - if (note != other.note) return false - if (note.id != other.note.id) return false - return true - } - - override fun hashCode(): Int { - var result = note.hashCode() - result = 31 * result + note.id.toInt() - return result - } - - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt deleted file mode 100644 index 32e2a4adf..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt +++ /dev/null @@ -1,92 +0,0 @@ -package io.github.wulkanowy.ui.modules.note - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.repositories.note.NoteRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import timber.log.Timber -import javax.inject.Inject - -class NotePresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val studentRepository: StudentRepository, - private val noteRepository: NoteRepository, - private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - override fun onAttachView(view: NoteView) { - super.onAttachView(view) - Timber.i("Note view is attached") - view.initView() - loadData() - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the note") - loadData(true) - } - - private fun loadData(forceRefresh: Boolean = false) { - Timber.i("Loading note data started") - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it).map { semester -> semester to it } } - .flatMap { noteRepository.getNotes(it.second, it.first, forceRefresh) } - .map { items -> items.map { NoteItem(it) } } - .map { items -> items.sortedByDescending { it.note.date } } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - } - }.subscribe({ - Timber.i("Loading note result: Success") - view?.apply { - updateData(it) - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - } - analytics.logEvent("load_note", "items" to it.size, "force_refresh" to forceRefresh) - }, { - Timber.i("Loading note result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - ) - } - - fun onNoteItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is NoteItem) { - Timber.i("Select note item ${item.note.id}") - view?.run { - showNoteDialog(item.note) - if (!item.note.isRead) { - item.note.isRead = true - updateItem(item) - updateNote(item.note) - } - } - } - } - - private fun updateNote(note: Note) { - Timber.i("Attempt to update note ${note.id}") - disposable.add(noteRepository.updateNote(note) - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ Timber.i("Update note result: Success") }) - { error -> - Timber.i("Update note result: An exception occurred") - errorHandler.dispatch(error) - }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt deleted file mode 100644 index 38e6c94fd..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.ui.modules.note - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface NoteView : BaseSessionView { - - val isViewEmpty: Boolean - - fun initView() - - fun updateData(data: List) - - fun updateItem(item: AbstractFlexibleItem<*>) - - fun clearData() - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun hideRefresh() - - fun showNoteDialog(note: Note) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt deleted file mode 100644 index d82eaba7e..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt +++ /dev/null @@ -1,76 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings - -import android.content.Context -import android.content.SharedPreferences -import android.os.Bundle -import androidx.appcompat.app.AppCompatDelegate -import com.takisoft.preferencex.PreferenceFragmentCompat -import dagger.android.support.AndroidSupportInjection -import io.github.wulkanowy.BuildConfig.DEBUG -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.BaseActivity -import io.github.wulkanowy.ui.modules.main.MainView -import javax.inject.Inject - -class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener, - MainView.TitledView, SettingsView { - - @Inject - lateinit var presenter: SettingsPresenter - - companion object { - fun newInstance() = SettingsFragment() - } - - override val titleStringId: Int - get() = R.string.settings_title - - override fun onAttach(context: Context) { - AndroidSupportInjection.inject(this) - super.onAttach(context) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this) - } - - override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.scheme_preferences) - findPreference(getString(R.string.pref_key_notification_debug)).isVisible = DEBUG - } - - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { - presenter.onSharedPreferenceChanged(key) - } - - override fun setTheme(theme: Int) { - AppCompatDelegate.setDefaultNightMode(theme) - activity?.recreate() - } - - override fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) { - findPreference(serviceEnablesKey).run { - summary = if (isHolidays) getString(R.string.pref_services_suspended) else "" - isEnabled = !isHolidays - } - } - - override fun showError(text: String, error: Throwable) { - (activity as? BaseActivity)?.showError(text, error) - } - - override fun showMessage(text: String) { - (activity as? BaseActivity)?.showMessage(text) - } - - override fun onResume() { - super.onResume() - preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this) - } - - override fun onPause() { - super.onPause() - preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt deleted file mode 100644 index 65e871ea3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings - -import com.readystatesoftware.chuck.api.ChuckCollector -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.services.sync.SyncManager -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.isHolidays -import org.threeten.bp.LocalDate.now -import timber.log.Timber -import javax.inject.Inject - -class SettingsPresenter @Inject constructor( - errorHandler: ErrorHandler, - private val preferencesRepository: PreferencesRepository, - private val analytics: FirebaseAnalyticsHelper, - private val syncManager: SyncManager, - private val chuckCollector: ChuckCollector -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: SettingsView) { - super.onAttachView(view) - Timber.i("Settings view is attached") - view.setServicesSuspended(preferencesRepository.serviceEnableKey, now().isHolidays) - } - - fun onSharedPreferenceChanged(key: String) { - Timber.i("Change settings $key") - preferencesRepository.apply { - when (key) { - serviceEnableKey -> syncManager.run { if (isServiceEnabled) startSyncWorker() else stopSyncWorker() } - servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startSyncWorker(true) - currentThemeKey -> view?.setTheme(currentTheme) - isDebugNotificationEnableKey -> chuckCollector.showNotification(isDebugNotificationEnable) - } - } - analytics.logEvent("setting_changed", "name" to key) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt deleted file mode 100644 index 0b3c2f70c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings - -import io.github.wulkanowy.ui.base.BaseView - -interface SettingsView : BaseView { - - fun setTheme(theme: Int) - - fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt deleted file mode 100644 index 2bd625332..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.wulkanowy.ui.modules.splash - -import android.os.Bundle -import android.widget.Toast -import android.widget.Toast.LENGTH_LONG -import io.github.wulkanowy.ui.base.BaseActivity -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.main.MainActivity -import javax.inject.Inject - -class SplashActivity : BaseActivity(), SplashView { - - @Inject - lateinit var presenter: SplashPresenter - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - presenter.onAttachView(this) - } - - override fun openLoginView() { - startActivity(LoginActivity.getStartIntent(this)) - finish() - } - - override fun openMainView() { - startActivity(MainActivity.getStartIntent(this)) - finish() - } - - override fun showError(text: String, error: Throwable) { - Toast.makeText(this, text, LENGTH_LONG).show() - } - - override fun onDestroy() { - presenter.onDetachView() - super.onDestroy() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt deleted file mode 100644 index b6bf0e2a3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.ui.modules.splash - -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.SchedulersProvider -import javax.inject.Inject - -class SplashPresenter @Inject constructor( - private val studentRepository: StudentRepository, - private val errorHandler: ErrorHandler, - private val schedulers: SchedulersProvider -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: SplashView) { - super.onAttachView(view) - disposable.add(studentRepository.isStudentSaved() - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ - view.apply { - if (it) openMainView() - else openLoginView() - } - }, { errorHandler.dispatch(it) })) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt deleted file mode 100644 index 9efd8123b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.ui.modules.splash - -import io.github.wulkanowy.ui.base.BaseView - -interface SplashView : BaseView { - - fun openLoginView() - - fun openMainView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt deleted file mode 100644 index 7e286c923..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt +++ /dev/null @@ -1,150 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -import android.annotation.SuppressLint -import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_timetable.* -import org.threeten.bp.LocalDateTime - -class TimetableDialog : DialogFragment() { - - private lateinit var lesson: Timetable - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(exam: Timetable): TimetableDialog { - return TimetableDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - lesson = getSerializable(ARGUMENT_KEY) as Timetable - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_timetable, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - lesson.run { - setInfo(info, teacher, canceled, changes) - setSubject(subject, subjectOld) - setTeacher(teacher, teacherOld) - setGroup(group) - setRoom(room, roomOld) - setTime(start, end) - } - - timetableDialogClose.setOnClickListener { dismiss() } - } - - private fun setSubject(subject: String, subjectOld: String) { - timetableDialogSubject.text = subject - if (subjectOld.isNotBlank() && subjectOld != subject) { - timetableDialogSubject.run { - paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG - text = subjectOld - } - timetableDialogSubjectNew.run { - visibility = VISIBLE - text = subject - } - } - } - - private fun setInfo(info: String, teacher: String, canceled: Boolean, changes: Boolean) { - when { - info.isNotBlank() -> timetableDialogChanges.text = when { - canceled && !changes -> "Lekcja odwołana: $info" - changes && teacher.isNotBlank() -> "Zastępstwo: $teacher" - changes && teacher.isBlank() -> "Zastępstwo, ${info.decapitalize()}" - else -> info.capitalize() - } - else -> { - timetableDialogChangesTitle.visibility = GONE - timetableDialogChanges.visibility = GONE - } - } - } - - private fun setTeacher(teacher: String, teacherOld: String) { - when { - teacherOld.isNotBlank() && teacherOld != teacher -> { - timetableDialogTeacher.run { - visibility = VISIBLE - paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG - text = teacherOld - } - if (teacher.isNotBlank()) { - timetableDialogTeacherNew.run { - visibility = VISIBLE - text = teacher - } - } - } - teacher.isNotBlank() -> timetableDialogTeacher.text = teacher - else -> { - timetableDialogTeacherTitle.visibility = GONE - timetableDialogTeacher.visibility = GONE - } - } - } - - private fun setGroup(group: String) { - group.let { - when { - it.isNotBlank() -> timetableDialogGroup.text = it - else -> { - timetableDialogGroupTitle.visibility = GONE - timetableDialogGroup.visibility = GONE - } - } - } - } - - private fun setRoom(room: String, roomOld: String) { - when { - roomOld.isNotBlank() && roomOld != room -> { - timetableDialogRoom.run { - visibility = VISIBLE - paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG - text = roomOld - } - if (room.isNotBlank()) { - timetableDialogRoomNew.run { - visibility = VISIBLE - text = room - } - } - } - room.isNotBlank() -> timetableDialogRoom.text = room - else -> { - timetableDialogRoomTitle.visibility = GONE - timetableDialogRoom.visibility = GONE - } - } - } - - @SuppressLint("SetTextI18n") - private fun setTime(start: LocalDateTime, end: LocalDateTime) { - timetableDialogTime.text = "${start.toFormattedString("HH:mm")} - ${end.toFormattedString("HH:mm")}" - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt deleted file mode 100644 index 3b1259f76..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt +++ /dev/null @@ -1,161 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_timetable.* -import javax.inject.Inject - -class TimetableFragment : BaseSessionFragment(), TimetableView, MainView.MainChildView, MainView.TitledView { - - @Inject - lateinit var presenter: TimetablePresenter - - @Inject - lateinit var timetableAdapter: FlexibleAdapter> - - companion object { - private const val SAVED_DATE_KEY = "CURRENT_DATE" - - fun newInstance() = TimetableFragment() - } - - override val titleStringId: Int - get() = R.string.timetable_title - - override val roomString: String - get() = getString(R.string.timetable_room) - - override val isViewEmpty: Boolean - get() = timetableAdapter.isEmpty - - override val currentStackSize: Int? - get() = (activity as? MainActivity)?.currentStackSize - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_timetable, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = timetableRecycler - presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) - } - - override fun initView() { - timetableAdapter.run { - setOnItemClickListener { presenter.onTimetableItemSelected(it) } - } - - timetableRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = timetableAdapter - addItemDecoration(FlexibleItemDecoration(context) - .withDefaultDivider() - .withDrawDividerOnLastItem(false) - ) - } - timetableSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - timetablePreviousButton.setOnClickListener { presenter.onPreviousDay() } - timetableNextButton.setOnClickListener { presenter.onNextDay() } - } - - override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { - inflater?.inflate(R.menu.action_menu_timetable, menu) - } - - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - return if (item?.itemId == R.id.timetableMenuCompletedLessons) presenter.onCompletedLessonsSwitchSelected() - else false - } - - override fun updateData(data: List) { - timetableAdapter.updateDataSet(data, true) - } - - override fun clearData() { - timetableAdapter.clear() - } - - override fun updateNavigationDay(date: String) { - timetableNavDate.text = date - } - - override fun hideRefresh() { - timetableSwipe.isRefreshing = false - } - - override fun resetView() { - timetableRecycler.smoothScrollToPosition(0) - } - - override fun onFragmentReselected() { - presenter.onViewReselected() - } - - override fun popView() { - (activity as? MainActivity)?.popView() - } - - override fun showEmpty(show: Boolean) { - timetableEmpty.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showProgress(show: Boolean) { - timetableProgress.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun enableSwipe(enable: Boolean) { - timetableSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - timetableRecycler.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showPreButton(show: Boolean) { - timetablePreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showNextButton(show: Boolean) { - timetableNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showTimetableDialog(lesson: Timetable) { - (activity as? MainActivity)?.showDialogFragment(TimetableDialog.newInstance(lesson)) - } - - override fun openCompletedLessonsView() { - (activity as? MainActivity)?.pushView(CompletedLessonsFragment.newInstance()) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt deleted file mode 100644 index c721401f3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt +++ /dev/null @@ -1,61 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -import android.annotation.SuppressLint -import android.graphics.Paint -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_timetable.* - -class TimetableItem(val lesson: Timetable, private val roomText: String) : - AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_timetable - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - timetableItemNumber.text = lesson.number.toString() - timetableItemSubject.text = lesson.subject - timetableItemRoom.text = if (lesson.room.isNotBlank()) "$roomText ${lesson.room}" else "" - timetableItemTime.text = "${lesson.start.toFormattedString("HH:mm")} - ${lesson.end.toFormattedString("HH:mm")}" - timetableItemAlert.visibility = if (lesson.changes || lesson.canceled) VISIBLE else GONE - timetableItemSubject.paintFlags = - if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG - else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as TimetableItem - - if (lesson != other.lesson) return false - return true - } - - override fun hashCode(): Int { - var result = lesson.hashCode() - result = 31 * result + lesson.id.toInt() - return result - } - - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt deleted file mode 100644 index ebda3931a..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ /dev/null @@ -1,134 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.timetable.TimetableRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.isHolidays -import io.github.wulkanowy.utils.nextOrSameSchoolDay -import io.github.wulkanowy.utils.nextSchoolDay -import io.github.wulkanowy.utils.previousSchoolDay -import io.github.wulkanowy.utils.toFormattedString -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDate.now -import org.threeten.bp.LocalDate.ofEpochDay -import timber.log.Timber -import java.util.concurrent.TimeUnit.MILLISECONDS -import javax.inject.Inject - -class TimetablePresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val timetableRepository: TimetableRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - lateinit var currentDate: LocalDate - private set - - fun onAttachView(view: TimetableView, date: Long?) { - super.onAttachView(view) - Timber.i("Timetable is attached") - view.initView() - loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay())) - reloadView() - } - - fun onPreviousDay() { - loadData(currentDate.previousSchoolDay) - reloadView() - } - - fun onNextDay() { - loadData(currentDate.nextSchoolDay) - reloadView() - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the timetable") - loadData(currentDate, true) - } - - fun onViewReselected() { - Timber.i("Timetable view is reselected") - view?.also { view -> - if (view.currentStackSize == 1) { - now().nextOrSameSchoolDay.also { - if (currentDate != it) { - loadData(it) - reloadView() - } else if (!view.isViewEmpty) view.resetView() - } - } else view.popView() - } - } - - fun onTimetableItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is TimetableItem) { - Timber.i("Select exam item ${item.lesson.id}") - view?.showTimetableDialog(item.lesson) - } - } - - fun onCompletedLessonsSwitchSelected(): Boolean { - view?.openCompletedLessonsView() - return true - } - - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - Timber.i("Loading timetable data started") - currentDate = date - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .delay(200, MILLISECONDS) - .flatMap { timetableRepository.getTimetable(it, currentDate, currentDate, forceRefresh) } - .map { items -> items.map { TimetableItem(it, view?.roomString.orEmpty()) } } - .map { items -> items.sortedBy { it.lesson.number } } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - } - } - .subscribe({ - Timber.i("Loading timetable result: Success") - view?.apply { - updateData(it) - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - } - analytics.logEvent("load_timetable", "items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd")) - }) { - Timber.i("Loading timetable result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } - } - - private fun reloadView() { - Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}") - view?.apply { - showProgress(true) - enableSwipe(false) - showContent(false) - showEmpty(false) - clearData() - showNextButton(!currentDate.plusDays(1).isHolidays) - showPreButton(!currentDate.minusDays(1).isHolidays) - updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize()) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt deleted file mode 100644 index c08961c36..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface TimetableView : BaseSessionView { - - val roomString: String - - val isViewEmpty: Boolean - - val currentStackSize: Int? - - fun initView() - - fun updateData(data: List) - - fun updateNavigationDay(date: String) - - fun clearData() - - fun hideRefresh() - - fun resetView() - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun showPreButton(show: Boolean) - - fun showNextButton(show: Boolean) - - fun showTimetableDialog(lesson: Timetable) - - fun popView() - - fun openCompletedLessonsView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt deleted file mode 100644 index 8f7b1ec5b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt +++ /dev/null @@ -1,73 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.completed - -import android.annotation.SuppressLint -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.CompletedLesson -import kotlinx.android.synthetic.main.dialog_lesson_completed.* - -class CompletedLessonDialog : DialogFragment() { - - private lateinit var completedLesson: CompletedLesson - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(exam: CompletedLesson): CompletedLessonDialog { - return CompletedLessonDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - completedLesson = getSerializable(CompletedLessonDialog.ARGUMENT_KEY) as CompletedLesson - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_lesson_completed, container, false) - } - - @SuppressLint("SetTextI18n") - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - completedLessonDialogSubject.text = completedLesson.subject - completedLessonDialogTopic.text = completedLesson.topic - completedLessonDialogTeacher.text = completedLesson.teacher - completedLessonDialogAbsence.text = completedLesson.absence - completedLessonDialogChanges.text = completedLesson.substitution - completedLessonDialogResources.text = completedLesson.resources - - completedLesson.substitution.let { - if (it.isBlank()) { - completedLessonDialogChangesTitle.visibility = View.GONE - completedLessonDialogChanges.visibility = View.GONE - } else completedLessonDialogChanges.text = it - } - - completedLesson.absence.let { - if (it.isBlank()) { - completedLessonDialogAbsenceTitle.visibility = View.GONE - completedLessonDialogAbsence.visibility = View.GONE - } else completedLessonDialogAbsence.text = it - } - - completedLesson.resources.let { - if (it.isBlank()) { - completedLessonDialogResourcesTitle.visibility = View.GONE - completedLessonDialogResources.visibility = View.GONE - } else completedLessonDialogResources.text = it - } - - completedLessonDialogClose.setOnClickListener { dismiss() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonItem.kt deleted file mode 100644 index 716903f55..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonItem.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.completed - -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.CompletedLesson -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_completed_lesson.* - -class CompletedLessonItem(val completedLesson: CompletedLesson) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_completed_lesson - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): CompletedLessonItem.ViewHolder { - return CompletedLessonItem.ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: CompletedLessonItem.ViewHolder?, position: Int, payloads: MutableList?) { - holder?.apply { - completedLessonItemNumber.text = completedLesson.number.toString() - completedLessonItemSubject.text = completedLesson.subject - completedLessonItemTopic.text = completedLesson.topic - completedLessonItemAlert.visibility = if (completedLesson.substitution.isNotEmpty()) VISIBLE else GONE - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as CompletedLessonItem - - if (completedLesson != other.completedLesson) return false - - return true - } - - override fun hashCode(): Int { - return completedLesson.hashCode() - } - - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt deleted file mode 100644 index 50d556859..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.completed - -import android.content.res.Resources -import com.readystatesoftware.chuck.api.ChuckCollector -import io.github.wulkanowy.api.interceptor.FeatureDisabledException -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import javax.inject.Inject - -class CompletedLessonsErrorHandler @Inject constructor( - resources: Resources, - chuckCollector: ChuckCollector -) : SessionErrorHandler(resources, chuckCollector) { - - var onFeatureDisabled: () -> Unit = {} - - override fun proceed(error: Throwable) { - when (error) { - is FeatureDisabledException -> onFeatureDisabled() - else -> super.proceed(error) - } - } - - override fun clear() { - super.clear() - onFeatureDisabled = {} - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt deleted file mode 100644 index bb328c414..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt +++ /dev/null @@ -1,124 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.completed - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.content.ContextCompat -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_timetable_completed.* -import javax.inject.Inject - -class CompletedLessonsFragment : BaseSessionFragment(), CompletedLessonsView, MainView.TitledView { - - @Inject - lateinit var presenter: CompletedLessonsPresenter - - @Inject - lateinit var completedLessonsAdapter: FlexibleAdapter> - - companion object { - private const val SAVED_DATE_KEY = "CURRENT_DATE" - - fun newInstance() = CompletedLessonsFragment() - } - - override val titleStringId: Int - get() = R.string.completed_lessons_title - - override val isViewEmpty - get() = completedLessonsAdapter.isEmpty - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_timetable_completed, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = completedLessonsRecycler - presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) - } - - override fun initView() { - completedLessonsAdapter.run { - setOnItemClickListener { presenter.onCompletedLessonsItemSelected(it) } - } - - completedLessonsRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = completedLessonsAdapter - } - completedLessonsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - completedLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() } - completedLessonsNextButton.setOnClickListener { presenter.onNextDay() } - } - - override fun updateData(data: List) { - completedLessonsAdapter.updateDataSet(data, true) - } - - override fun clearData() { - completedLessonsAdapter.clear() - } - - override fun updateNavigationDay(date: String) { - completedLessonsNavDate.text = date - } - - override fun hideRefresh() { - completedLessonsSwipe.isRefreshing = false - } - - override fun showEmpty(show: Boolean) { - completedLessonsEmpty.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showFeatureDisabled() { - context?.let { - completedLessonsInfo.text = getString(R.string.error_feature_disabled) - completedLessonsInfoImage.setImageDrawable(ContextCompat.getDrawable(it, R.drawable.ic_all_close_circle_24dp)) - } - } - - override fun showProgress(show: Boolean) { - completedLessonsProgress.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun enableSwipe(enable: Boolean) { - completedLessonsSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - completedLessonsRecycler.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showPreButton(show: Boolean) { - completedLessonsPreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showNextButton(show: Boolean) { - completedLessonsNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showCompletedLessonDialog(completedLesson: CompletedLesson) { - (activity as? MainActivity)?.showDialogFragment(CompletedLessonDialog.newInstance(completedLesson)) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putLong(CompletedLessonsFragment.SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt deleted file mode 100644 index 6a14acda5..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt +++ /dev/null @@ -1,118 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.completed - -import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.isHolidays -import io.github.wulkanowy.utils.nextOrSameSchoolDay -import io.github.wulkanowy.utils.nextSchoolDay -import io.github.wulkanowy.utils.previousSchoolDay -import io.github.wulkanowy.utils.toFormattedString -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDate.now -import org.threeten.bp.LocalDate.ofEpochDay -import timber.log.Timber -import java.util.concurrent.TimeUnit -import javax.inject.Inject - -class CompletedLessonsPresenter @Inject constructor( - private val schedulers: SchedulersProvider, - private val errorHandler: CompletedLessonsErrorHandler, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val completedLessonsRepository: CompletedLessonsRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - lateinit var currentDate: LocalDate - private set - - fun onAttachView(view: CompletedLessonsView, date: Long?) { - super.onAttachView(view) - Timber.i("Completed lessons is attached") - view.initView() - loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay())) - reloadView() - errorHandler.onFeatureDisabled = { - this.view?.showFeatureDisabled() - Timber.i("Completed lessons feature disabled by school") - } - } - - fun onPreviousDay() { - loadData(currentDate.previousSchoolDay) - reloadView() - } - - fun onNextDay() { - loadData(currentDate.nextSchoolDay) - reloadView() - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the completed lessons") - loadData(currentDate, true) - } - - fun onCompletedLessonsItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is CompletedLessonItem) { - Timber.i("Select completed lessons item ${item.completedLesson.id}") - view?.showCompletedLessonDialog(item.completedLesson) - } - } - - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - Timber.i("Loading completed lessons data started") - currentDate = date - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .delay(200, TimeUnit.MILLISECONDS) - .flatMap { completedLessonsRepository.getCompletedLessons(it, currentDate, currentDate, forceRefresh) } - .map { items -> items.map { CompletedLessonItem(it) } } - .map { items -> items.sortedBy { it.completedLesson.number } } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - } - } - .subscribe({ - Timber.i("Loading completed lessons lessons result: Success") - view?.apply { - updateData(it) - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - } - analytics.logEvent("load_completed_lessons", "items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd")) - }) { - Timber.i("Loading completed lessons result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } - } - - private fun reloadView() { - Timber.i("Reload completed lessons view with the date ${currentDate.toFormattedString()}") - view?.apply { - showProgress(true) - enableSwipe(false) - showContent(false) - showEmpty(false) - clearData() - showNextButton(!currentDate.plusDays(1).isHolidays) - showPreButton(!currentDate.minusDays(1).isHolidays) - updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize()) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt deleted file mode 100644 index 9607cc476..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.completed - -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface CompletedLessonsView : BaseSessionView { - - val isViewEmpty: Boolean - - fun initView() - - fun updateData(data: List) - - fun clearData() - - fun updateNavigationDay(date: String) - - fun hideRefresh() - - fun showEmpty(show: Boolean) - - fun showFeatureDisabled() - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun showPreButton(show: Boolean) - - fun showNextButton(show: Boolean) - - fun showCompletedLessonDialog(completedLesson: CompletedLesson) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashActivity.java b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashActivity.java new file mode 100644 index 000000000..c9f98d5ee --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashActivity.java @@ -0,0 +1,50 @@ +package io.github.wulkanowy.ui.splash; + +import android.os.Bundle; + +import javax.inject.Inject; + +import butterknife.ButterKnife; +import io.github.wulkanowy.services.SyncJob; +import io.github.wulkanowy.ui.base.BaseActivity; +import io.github.wulkanowy.ui.login.LoginActivity; +import io.github.wulkanowy.ui.main.MainActivity; + +public class SplashActivity extends BaseActivity implements SplashContract.View { + + @Inject + SplashContract.Presenter presenter; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getActivityComponent().inject(this); + setButterKnife(ButterKnife.bind(this)); + + presenter.onStart(this); + } + + @Override + protected void onDestroy() { + presenter.onDestroy(); + super.onDestroy(); + } + + @Override + public void openLoginActivity() { + startActivity(LoginActivity.getStartIntent(this)); + finish(); + } + + @Override + public void openMainActivity() { + startActivity(MainActivity.getStartIntent(this)); + finish(); + } + + @Override + public void startSyncService() { + SyncJob.start(getApplicationContext()); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashContract.java b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashContract.java new file mode 100644 index 000000000..a69deb032 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashContract.java @@ -0,0 +1,21 @@ +package io.github.wulkanowy.ui.splash; + + +import io.github.wulkanowy.di.annotations.PerActivity; +import io.github.wulkanowy.ui.base.BaseContract; + +public interface SplashContract { + + interface View extends BaseContract.View { + + void openLoginActivity(); + + void openMainActivity(); + + void startSyncService(); + } + + @PerActivity + interface Presenter extends BaseContract.Presenter { + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.java new file mode 100644 index 000000000..d617b4afa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.java @@ -0,0 +1,35 @@ +package io.github.wulkanowy.ui.splash; + +import android.support.annotation.NonNull; + +import javax.inject.Inject; + +import io.github.wulkanowy.data.RepositoryContract; +import io.github.wulkanowy.ui.base.BasePresenter; +import io.github.wulkanowy.utils.LogUtils; + +public class SplashPresenter extends BasePresenter + implements SplashContract.Presenter { + + @Inject + SplashPresenter(RepositoryContract repository) { + super(repository); + } + + @Override + public void onStart(@NonNull SplashContract.View activity) { + super.onStart(activity); + getView().startSyncService(); + + if (getRepository().getCurrentUserId() == 0) { + getView().openLoginActivity(); + } else { + try { + getRepository().initLastUser(); + getView().openMainActivity(); + } catch (Exception e) { + LogUtils.error("An error occurred when the application was started", e); + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt deleted file mode 100644 index f2d40ba7e..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt +++ /dev/null @@ -1,107 +0,0 @@ -package io.github.wulkanowy.ui.widgets.timetable - -import android.content.Context -import android.content.Intent -import android.graphics.Paint.ANTI_ALIAS_FLAG -import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG -import android.view.View.GONE -import android.view.View.VISIBLE -import android.widget.AdapterView.INVALID_POSITION -import android.widget.RemoteViews -import android.widget.RemoteViewsService -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.SharedPrefHelper -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.timetable.TimetableRepository -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.toFormattedString -import io.reactivex.Single -import org.threeten.bp.LocalDate -import timber.log.Timber - -class TimetableWidgetFactory( - private val timetableRepository: TimetableRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val sharedPref: SharedPrefHelper, - private val schedulers: SchedulersProvider, - private val context: Context, - private val intent: Intent? -) : RemoteViewsService.RemoteViewsFactory { - - private var lessons = emptyList() - - override fun getLoadingView() = null - - override fun hasStableIds() = true - - override fun getCount() = lessons.size - - override fun getViewTypeCount() = 1 - - override fun getItemId(position: Int) = position.toLong() - - override fun onCreate() {} - - override fun onDestroy() {} - - override fun onDataSetChanged() { - intent?.action?.let { LocalDate.ofEpochDay(sharedPref.getLong(it, 0)) } - ?.let { date -> - try { - lessons = studentRepository.isStudentSaved() - .flatMap { isSaved -> - if (isSaved) { - studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { timetableRepository.getTimetable(it, date, date) } - } else Single.just(emptyList()) - } - .map { item -> item.sortedBy { it.number } } - .subscribeOn(schedulers.backgroundThread) - .blockingGet() - } catch (e: Exception) { - Timber.e(e, "An error has occurred while downloading data for the widget") - } - } - } - - override fun getViewAt(position: Int): RemoteViews? { - if (position == INVALID_POSITION || lessons.getOrNull(position) === null) return null - - return RemoteViews(context.packageName, R.layout.item_widget_timetable).apply { - lessons[position].let { - setTextViewText(R.id.timetableWidgetItemSubject, it.subject) - setTextViewText(R.id.timetableWidgetItemNumber, it.number.toString()) - setTextViewText(R.id.timetableWidgetItemTime, it.start.toFormattedString("HH:mm") + - " - ${it.end.toFormattedString("HH:mm")}") - - if (it.room.isNotBlank()) { - setTextViewText(R.id.timetableWidgetItemRoom, "${context.getString(R.string.timetable_room)} ${it.room}") - } else setTextViewText(R.id.timetableWidgetItemRoom, "") - - if (it.info.isNotBlank()) { - setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE) - setTextViewText(R.id.timetableWidgetItemDescription, it.run { - when (true) { - canceled && !changes -> "Lekcja odwołana: $info" - changes && teacher.isNotBlank() -> "Zastępstwo: $teacher" - changes && teacher.isBlank() -> "Zastępstwo, ${info.decapitalize()}" - else -> it.info.capitalize() - } - }) - } else setViewVisibility(R.id.timetableWidgetItemDescription, GONE) - - if (it.canceled) { - setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", - STRIKE_THRU_TEXT_FLAG or ANTI_ALIAS_FLAG) - } else { - setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", ANTI_ALIAS_FLAG) - } - } - setOnClickFillInIntent(R.id.timetableWidgetItemContainer, Intent()) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt deleted file mode 100644 index 402366ecf..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt +++ /dev/null @@ -1,123 +0,0 @@ -package io.github.wulkanowy.ui.widgets.timetable - -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.appwidget.AppWidgetManager -import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_DELETED -import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE -import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID -import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.widget.RemoteViews -import dagger.android.AndroidInjection -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.SharedPrefHelper -import io.github.wulkanowy.services.widgets.TimetableWidgetService -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.nextOrSameSchoolDay -import io.github.wulkanowy.utils.nextSchoolDay -import io.github.wulkanowy.utils.previousSchoolDay -import io.github.wulkanowy.utils.toFormattedString -import io.github.wulkanowy.utils.weekDayName -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDate.now -import javax.inject.Inject - -class TimetableWidgetProvider : BroadcastReceiver() { - - @Inject - lateinit var appWidgetManager: AppWidgetManager - - @Inject - lateinit var sharedPref: SharedPrefHelper - - @Inject - lateinit var analytics: FirebaseAnalyticsHelper - - companion object { - const val EXTRA_TOGGLED_WIDGET_ID = "extraToggledWidget" - - const val EXTRA_BUTTON_TYPE = "extraButtonType" - - const val BUTTON_NEXT = "buttonNext" - - const val BUTTON_PREV = "buttonPrev" - - const val BUTTON_RESET = "buttonReset" - - fun createWidgetKey(appWidgetId: Int) = "timetable_widget_$appWidgetId" - } - - override fun onReceive(context: Context, intent: Intent) { - AndroidInjection.inject(this, context) - when (intent.action) { - ACTION_APPWIDGET_UPDATE -> onUpdate(context, intent) - ACTION_APPWIDGET_DELETED -> onDelete(intent) - } - } - - private fun onUpdate(context: Context, intent: Intent) { - if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) { - intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId -> - updateWidget(context, appWidgetId, now().nextOrSameSchoolDay) - } - } else { - val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE) - val toggledWidgetId = intent.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0) - val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(createWidgetKey(toggledWidgetId), 0)) - val date = when (buttonType) { - BUTTON_RESET -> now().nextOrSameSchoolDay - BUTTON_NEXT -> savedDate.nextSchoolDay - BUTTON_PREV -> savedDate.previousSchoolDay - else -> now().nextOrSameSchoolDay - } - if (!buttonType.isNullOrBlank()) analytics.logEvent("changed_timetable_widget_day", "button" to buttonType) - updateWidget(context, toggledWidgetId, date) - } - } - - private fun onDelete(intent: Intent) { - intent.getIntExtra(EXTRA_APPWIDGET_ID, 0).let { - if (it != 0) sharedPref.delete(createWidgetKey(it)) - } - } - - private fun updateWidget(context: Context, appWidgetId: Int, date: LocalDate) { - RemoteViews(context.packageName, R.layout.widget_timetable).apply { - setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty) - setTextViewText(R.id.timetableWidgetDay, date.weekDayName.capitalize()) - setTextViewText(R.id.timetableWidgetDate, date.toFormattedString()) - setRemoteAdapter(R.id.timetableWidgetList, Intent(context, TimetableWidgetService::class.java) - .apply { action = createWidgetKey(appWidgetId) }) - setOnClickPendingIntent(R.id.timetableWidgetNext, createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT)) - setOnClickPendingIntent(R.id.timetableWidgetPrev, createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV)) - createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET).also { - setOnClickPendingIntent(R.id.timetableWidgetDate, it) - setOnClickPendingIntent(R.id.timetableWidgetDay, it) - } - setPendingIntentTemplate(R.id.timetableWidgetList, - PendingIntent.getActivity(context, 1, MainActivity.getStartIntent(context).apply { - putExtra(EXTRA_START_MENU_INDEX, 3) - }, FLAG_UPDATE_CURRENT)) - }.also { - sharedPref.putLong(createWidgetKey(appWidgetId), date.toEpochDay(), true) - appWidgetManager.apply { - notifyAppWidgetViewDataChanged(appWidgetId, R.id.timetableWidgetList) - updateAppWidget(appWidgetId, it) - } - } - } - - private fun createNavIntent(context: Context, code: Int, appWidgetId: Int, buttonType: String): PendingIntent { - return PendingIntent.getBroadcast(context, code, - Intent(context, TimetableWidgetProvider::class.java).apply { - action = ACTION_APPWIDGET_UPDATE - putExtra(EXTRA_BUTTON_TYPE, buttonType) - putExtra(EXTRA_TOGGLED_WIDGET_ID, appWidgetId) - }, FLAG_UPDATE_CURRENT) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ActivityExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ActivityExtension.kt deleted file mode 100644 index c0314d02e..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/ActivityExtension.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.utils - -import android.app.Activity -import android.content.Context.INPUT_METHOD_SERVICE -import android.view.inputmethod.InputMethodManager - - -fun Activity.showSoftInput() { - (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager?)?.run { - if (currentFocus != null) showSoftInput(currentFocus, 0) - } -} - -fun Activity.hideSoftInput() { - (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager?)?.run { - hideSoftInputFromWindow(window.decorView.applicationWindowToken, 0) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/AppConstant.java b/app/src/main/java/io/github/wulkanowy/utils/AppConstant.java new file mode 100644 index 000000000..27d76e756 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/AppConstant.java @@ -0,0 +1,25 @@ +package io.github.wulkanowy.utils; + +public final class AppConstant { + + public static final String APP_NAME = "Wulkanowy"; + + public static final String DATABASE_NAME = "wulkanowy_db"; + + public static final String SHARED_PREFERENCES_NAME = "user_data"; + + + public static final String VULCAN_CREATE_ACCOUNT_URL = + "https://cufs.vulcan.net.pl/Default/AccountManage/CreateAccount"; + + public static final String VULCAN_FORGOT_PASS_URL = + "https://cufs.vulcan.net.pl/Default/AccountManage/UnlockAccount"; + + public static final String DEFAULT_SYMBOL = "Default"; + + public static final String DATE_PATTERN = "yyyy-MM-dd"; + + private AppConstant() { + throw new IllegalStateException("Utility class"); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt deleted file mode 100644 index c2b1efaa8..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.utils - -import io.github.wulkanowy.data.db.entities.AttendanceSummary - -/** - * [UONET+ - Zasady tworzenia podsumowań liczb uczniów obecnych i nieobecnych w tabeli frekwencji] - * (https://www.vulcan.edu.pl/vulcang_files/user/AABW/AABW-PDF/uonetplus/uonetplus_Frekwencja-liczby-obecnych-nieobecnych.pdf) - */ - -private inline val AttendanceSummary.allPresences: Double - get() = presence.toDouble() + absenceForSchoolReasons + lateness + latenessExcused - -private inline val AttendanceSummary.allAbsences: Double - get() = absence.toDouble() + absenceExcused - -fun AttendanceSummary.calculatePercentage() = calculatePercentage(allPresences, allAbsences) - -fun List.calculatePercentage(): Double { - return calculatePercentage(sumByDouble { it.allPresences }, sumByDouble { it.allAbsences }) -} - -private fun calculatePercentage(presence: Double, absence: Double): Double { - return if ((presence + absence) == 0.0) 0.0 else (presence / (presence + absence)) * 100 -} - - diff --git a/app/src/main/java/io/github/wulkanowy/utils/AverageCalculator.java b/app/src/main/java/io/github/wulkanowy/utils/AverageCalculator.java new file mode 100644 index 000000000..cc33e65a9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/AverageCalculator.java @@ -0,0 +1,61 @@ +package io.github.wulkanowy.utils; + +import java.util.List; + +import io.github.wulkanowy.data.db.dao.entities.Grade; + +public final class AverageCalculator { + + private AverageCalculator() { + throw new IllegalStateException("Utility class"); + } + + public static float calculate(List gradeList) { + + float counter = 0f; + float denominator = 0f; + + for (Grade grade : gradeList) { + int integerWeight = getIntegerForWeightOfGrade(grade.getWeight()); + float floatValue = getMathematicalValueOfGrade(grade.getValue()); + + if (floatValue != -1f) { + counter += floatValue * integerWeight; + denominator += integerWeight; + } + } + + if (counter == 0f) { + return -1f; + } else { + return counter / denominator; + } + } + + private static float getMathematicalValueOfGrade(String valueOfGrade) { + if (valueOfGrade.matches("[-|+|=]{0,2}[0-6]") + || valueOfGrade.matches("[0-6][-|+|=]{0,2}")) { + if (valueOfGrade.matches("[-][0-6]") + || valueOfGrade.matches("[0-6][-]")) { + String replacedValue = valueOfGrade.replaceAll("[-]", ""); + return Float.valueOf(replacedValue) - 0.33f; + } else if (valueOfGrade.matches("[+][0-6]") + || valueOfGrade.matches("[0-6][+]")) { + String replacedValue = valueOfGrade.replaceAll("[+]", ""); + return Float.valueOf((replacedValue)) + 0.33f; + } else if (valueOfGrade.matches("[-|=]{1,2}[0-6]") + || valueOfGrade.matches("[0-6][-|=]{1,2}")) { + String replacedValue = valueOfGrade.replaceAll("[-|=]{1,2}", ""); + return Float.valueOf((replacedValue)) - 0.5f; + } else { + return Float.valueOf(valueOfGrade); + } + } else { + return -1; + } + } + + private static int getIntegerForWeightOfGrade(String weightOfGrade) { + return Integer.valueOf(weightOfGrade.substring(0, weightOfGrade.length() - 3)); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java b/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java new file mode 100644 index 000000000..8ec73faf7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java @@ -0,0 +1,40 @@ +package io.github.wulkanowy.utils; + +import android.app.Activity; +import android.net.Uri; +import android.support.customtabs.CustomTabsIntent; + +import io.github.wulkanowy.R; + +public final class CommonUtils { + + private CommonUtils() { + throw new IllegalStateException("Utility class"); + } + + public static void openInternalBrowserViewer(Activity activity, String url) { + CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); + builder.setToolbarColor(activity.getResources().getColor(R.color.colorPrimary)); + CustomTabsIntent customTabsIntent = builder.build(); + customTabsIntent.launchUrl(activity, Uri.parse(url)); + } + + public static int colorHexToColorName(String hexColor) { + switch (hexColor) { + case "000000": + return R.string.color_black_text; + + 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; + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt deleted file mode 100644 index 70368badd..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.utils - -import android.content.Context -import androidx.annotation.AttrRes -import androidx.annotation.ColorInt -import androidx.annotation.ColorRes -import androidx.core.content.ContextCompat - -@ColorInt -fun Context.getThemeAttrColor(@AttrRes colorAttr: Int): Int { - val array = obtainStyledAttributes(null, intArrayOf(colorAttr)) - return try { - array.getColor(0, 0) - } finally { - array.recycle() - } -} - -@ColorInt -fun Context.getCompatColor(@ColorRes colorRes: Int) = ContextCompat.getColor(this, colorRes) diff --git a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java new file mode 100644 index 000000000..a22e4d8b2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java @@ -0,0 +1,131 @@ +package io.github.wulkanowy.utils; + + +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; +import io.github.wulkanowy.data.db.dao.entities.Day; +import io.github.wulkanowy.data.db.dao.entities.Grade; +import io.github.wulkanowy.data.db.dao.entities.Subject; +import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; +import io.github.wulkanowy.data.db.dao.entities.Week; + +public final class DataObjectConverter { + + private DataObjectConverter() { + throw new IllegalStateException("Utility class"); + } + + public static List subjectsToSubjectEntities(List subjectList) { + + List subjectEntityList = new ArrayList<>(); + + for (io.github.wulkanowy.api.grades.Subject subject : subjectList) { + Subject subjectEntity = new Subject() + .setName(subject.getName()) + .setPredictedRating(subject.getPredictedRating()) + .setFinalRating(subject.getFinalRating()); + subjectEntityList.add(subjectEntity); + } + + return subjectEntityList; + } + + public static List gradesToGradeEntities(List gradeList) { + + List gradeEntityList = new ArrayList<>(); + + for (io.github.wulkanowy.api.grades.Grade grade : gradeList) { + Grade gradeEntity = new Grade() + .setSubject(grade.getSubject()) + .setValue(grade.getValue()) + .setColor(grade.getColor()) + .setSymbol(grade.getSymbol()) + .setDescription(grade.getDescription()) + .setWeight(grade.getWeight()) + .setDate(grade.getDate()) + .setTeacher(grade.getTeacher()) + .setSemester(grade.getSemester()); + + gradeEntityList.add(gradeEntity); + } + return gradeEntityList; + } + + public static Week weekToWeekEntity(io.github.wulkanowy.api.generic.Week week) { + return new Week().setStartDayDate(week.getStartDayDate()); + } + + public static Day dayToDayEntity(io.github.wulkanowy.api.generic.Day day) { + return new Day() + .setDate(day.getDate()) + .setDayName(day.getDayName()) + .setIsFreeDay(day.isFreeDay()) + .setFreeDayName(day.getFreeDayName()); + } + + + public static List daysToDaysEntities(List dayList) { + + List dayEntityList = new ArrayList<>(); + + for (io.github.wulkanowy.api.generic.Day day : dayList) { + dayEntityList.add(dayToDayEntity(day)); + } + return dayEntityList; + } + + public static TimetableLesson lessonToTimetableLessonEntity(io.github.wulkanowy.api.generic.Lesson lesson) { + return new TimetableLesson() + .setNumber(lesson.getNumber()) + .setSubject(lesson.getSubject()) + .setTeacher(lesson.getTeacher()) + .setRoom(lesson.getRoom()) + .setDescription(lesson.getDescription()) + .setGroupName(lesson.getGroupName()) + .setStartTime(lesson.getStartTime()) + .setEndTime(lesson.getEndTime()) + .setDate(lesson.getDate()) + .setEmpty(lesson.isEmpty()) + .setDivisionIntoGroups(lesson.isDivisionIntoGroups()) + .setPlanning(lesson.isPlanning()) + .setRealized(lesson.isRealized()) + .setMovedOrCanceled(lesson.isMovedOrCanceled()) + .setNewMovedInOrChanged(lesson.isNewMovedInOrChanged()); + } + + public static AttendanceLesson lessonToAttendanceLessonEntity(io.github.wulkanowy.api.generic.Lesson lesson) { + return new AttendanceLesson() + .setNumber(Integer.valueOf(lesson.getNumber())) + .setSubject(lesson.getSubject()) + .setDate(lesson.getDate()) + .setIsPresence(lesson.isPresence()) + .setIsAbsenceUnexcused(lesson.isAbsenceUnexcused()) + .setIsAbsenceExcused(lesson.isAbsenceExcused()) + .setIsUnexcusedLateness(lesson.isUnexcusedLateness()) + .setIsAbsenceForSchoolReasons(lesson.isAbsenceForSchoolReasons()) + .setIsExcusedLateness(lesson.isExcusedLateness()) + .setIsExemption(lesson.isExemption()); + } + + public static List lessonsToTimetableLessonsEntities(List lessonList) { + + List lessonEntityList = new ArrayList<>(); + + for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) { + lessonEntityList.add(lessonToTimetableLessonEntity(lesson)); + } + return lessonEntityList; + } + + public static List lessonsToAttendanceLessonsEntities(List lessonList) { + + List lessonEntityList = new ArrayList<>(); + + for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) { + lessonEntityList.add(lessonToAttendanceLessonEntity(lesson)); + } + return lessonEntityList; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/EditTextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/EditTextExtension.kt deleted file mode 100644 index caa977ec3..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/EditTextExtension.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.wulkanowy.utils - -import android.text.Editable -import android.text.TextWatcher -import android.widget.EditText - -inline fun EditText.setOnTextChangedListener(crossinline listener: () -> Unit) { - addTextChangedListener(object : TextWatcher { - override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - listener() - } - - override fun afterTextChanged(s: Editable?) {} - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} - }) -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/EntitiesCompare.java b/app/src/main/java/io/github/wulkanowy/utils/EntitiesCompare.java new file mode 100644 index 000000000..5681bad17 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/EntitiesCompare.java @@ -0,0 +1,42 @@ +package io.github.wulkanowy.utils; + +import org.apache.commons.collections4.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.data.db.dao.entities.Grade; + +public final class EntitiesCompare { + + private EntitiesCompare() { + throw new IllegalStateException("Utility class"); + } + + public static List compareGradeList(List newList, List oldList) { + + List addedOrUpdatedGradeList = new ArrayList<>(CollectionUtils + .removeAll(newList, oldList)); + List updatedList = new ArrayList<>(CollectionUtils + .removeAll(newList, addedOrUpdatedGradeList)); + List lastList = new ArrayList<>(); + + for (Grade grade : addedOrUpdatedGradeList) { + if (!oldList.isEmpty()) { + grade.setRead(false); + } + grade.setIsNew(true); + updatedList.add(grade); + } + + for (Grade updateGrade : updatedList) { + for (Grade oldGrade : oldList) { + if (updateGrade.equals(oldGrade)) { + updateGrade.setRead(oldGrade.getRead()); + } + } + lastList.add(updateGrade); + } + return lastList; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt b/app/src/main/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt deleted file mode 100644 index feb33e5d7..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.utils - -import android.os.Bundle -import com.google.firebase.analytics.FirebaseAnalytics -import javax.inject.Singleton - -@Singleton -class FirebaseAnalyticsHelper(private val analytics: FirebaseAnalytics) { - - fun logEvent(name: String, vararg params: Pair) { - Bundle().apply { - params.forEach { - if (it.second == null) return@forEach - when (it.second) { - is String, is String? -> putString(it.first, it.second as String) - is Int, is Int? -> putInt(it.first, it.second as Int) - is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean) - } - } - analytics.logEvent(name, this) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/FlexibleAdapterExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/FlexibleAdapterExtension.kt deleted file mode 100644 index bd6867a38..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/FlexibleAdapterExtension.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.utils - -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem - -inline fun FlexibleAdapter<*>.setOnItemClickListener(crossinline listener: (item: AbstractFlexibleItem<*>) -> Unit) { - addListener(FlexibleAdapter.OnItemClickListener { _, position -> - listener(getItem(position) as AbstractFlexibleItem<*>) - true - }) -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt deleted file mode 100644 index dcf477548..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.utils - -import androidx.fragment.app.Fragment -import com.ncapdevi.fragnav.FragNavController - -inline fun FragNavController.setOnViewChangeListener(crossinline listener: (fragment: Fragment?) -> Unit) { - transactionListener = object : FragNavController.TransactionListener { - override fun onFragmentTransaction(fragment: Fragment?, transactionType: FragNavController.TransactionType) { - listener(fragment) - } - - override fun onTabTransaction(fragment: Fragment?, index: Int) { - listener(fragment) - } - } -} - -fun FragNavController.safelyPopFragment() { - if (!isRootFragment) popFragment() -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt deleted file mode 100644 index c863b030f..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt +++ /dev/null @@ -1,74 +0,0 @@ -package io.github.wulkanowy.utils - -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.GradeSummary - -fun List.calcAverage(): Double { - var counter = 0.0 - var denominator = 0.0 - - forEach { - counter += (it.value + it.modifier) * it.weightValue - denominator += it.weightValue - } - return if (denominator != 0.0) counter / denominator else 0.0 -} - -@JvmName("calcSummaryAverage") -fun List.calcAverage(): Double { - return asSequence().mapNotNull { - if (it.finalGrade.matches("[0-6]".toRegex())) it.finalGrade.toDouble() else null - }.average().let { if (it.isNaN()) 0.0 else it } -} - -fun Grade.getBackgroundColor(theme: String): Int { - return when (theme) { - "grade_color" -> when (color) { - "000000" -> R.color.grade_black - "F04C4C" -> R.color.grade_red - "20A4F7" -> R.color.grade_blue - "6ECD07" -> R.color.grade_green - "B16CF1" -> R.color.grade_purple - else -> R.color.grade_material_default - } - "material" -> when (value) { - 6 -> R.color.grade_material_six - 5 -> R.color.grade_material_five - 4 -> R.color.grade_material_four - 3 -> R.color.grade_material_three - 2 -> R.color.grade_material_two - 1 -> R.color.grade_material_one - else -> R.color.grade_material_default - } - else -> when (value) { - 6 -> R.color.grade_vulcan_six - 5 -> R.color.grade_vulcan_five - 4 -> R.color.grade_vulcan_four - 3 -> R.color.grade_vulcan_three - 2 -> R.color.grade_vulcan_two - 1 -> R.color.grade_vulcan_one - else -> R.color.grade_vulcan_default - } - } -} - -inline val Grade.colorStringId: Int - get() { - return when (color) { - "000000" -> R.string.all_black - "F04C4C" -> R.string.all_red - "20A4F7" -> R.string.all_blue - "6ECD07" -> R.string.all_green - "B16CF1" -> R.string.all_purple - else -> R.string.all_empty_color - } - } - -fun Grade.changeModifier(plusModifier: Double, minusModifier: Double): Grade { - return when { - modifier != .0 && plusModifier != .0 && modifier > 0 -> copy(modifier = plusModifier) - modifier != .0 && minusModifier != .0 && modifier < 0 -> copy(modifier = -minusModifier) - else -> this - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/KeyboardUtils.java b/app/src/main/java/io/github/wulkanowy/utils/KeyboardUtils.java new file mode 100644 index 000000000..6be1b76f4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/KeyboardUtils.java @@ -0,0 +1,33 @@ +package io.github.wulkanowy.utils; + +import android.app.Activity; +import android.content.Context; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +public final class KeyboardUtils { + + private KeyboardUtils() { + throw new IllegalStateException("Utility class"); + } + + public static void hideSoftInput(Activity activity) { + InputMethodManager manager = (InputMethodManager) + activity.getSystemService(Context.INPUT_METHOD_SERVICE); + if (manager != null) { + manager.hideSoftInputFromWindow(activity.getWindow() + .getDecorView().getApplicationWindowToken(), 0); + } + } + + public static void showSoftInput(EditText editText, Context context) { + editText.setFocusable(true); + editText.setFocusableInTouchMode(true); + editText.requestFocus(); + InputMethodManager inputMethodManager = (InputMethodManager) context + .getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputMethodManager != null) { + inputMethodManager.showSoftInput(editText, 0); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/utils/LibsBuiderExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/LibsBuiderExtension.kt deleted file mode 100644 index d21b998bb..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/LibsBuiderExtension.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.wulkanowy.utils - -import android.view.View -import com.mikepenz.aboutlibraries.Libs -import com.mikepenz.aboutlibraries.LibsBuilder -import com.mikepenz.aboutlibraries.LibsConfiguration - -inline fun LibsBuilder.withOnExtraListener(crossinline listener: (Libs.SpecialButton?) -> Unit): LibsBuilder { - withListener(object : LibsConfiguration.LibsListenerImpl() { - override fun onExtraClicked(v: View?, specialButton: Libs.SpecialButton?): Boolean { - listener(specialButton) - return true - } - }) - return this -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/LogUtils.java b/app/src/main/java/io/github/wulkanowy/utils/LogUtils.java new file mode 100644 index 000000000..f59bbf641 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/LogUtils.java @@ -0,0 +1,26 @@ +package io.github.wulkanowy.utils; + +import android.util.Log; + +public final class LogUtils { + + private LogUtils() { + throw new IllegalStateException("Utility class"); + } + + public static void debug(String message) { + Log.d(AppConstant.APP_NAME, message); + } + + public static void error(String message, Throwable throwable) { + Log.e(AppConstant.APP_NAME, message, throwable); + } + + public static void error(String message) { + Log.e(AppConstant.APP_NAME, message); + } + + public static void info(String message) { + Log.i(AppConstant.APP_NAME, message); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt deleted file mode 100644 index 565374130..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.wulkanowy.utils - -import com.crashlytics.android.Crashlytics -import timber.log.Timber - -class DebugLogTree : Timber.DebugTree() { - - override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - super.log(priority, "Wulkanowy", message, t) - } -} - -class CrashlyticsTree : Timber.Tree() { - - override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - Crashlytics.setInt("priority", priority) - Crashlytics.setString("tag", tag) - - if (t == null) Crashlytics.log(message) - else Crashlytics.logException(t) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/NetworkUtils.java b/app/src/main/java/io/github/wulkanowy/utils/NetworkUtils.java new file mode 100644 index 000000000..a1484eb9e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/NetworkUtils.java @@ -0,0 +1,18 @@ +package io.github.wulkanowy.utils; + +import android.content.Context; +import android.net.ConnectivityManager; + +public final class NetworkUtils { + + private NetworkUtils() { + throw new IllegalStateException("Utility class"); + } + + public static boolean isOnline(Context context) { + ConnectivityManager connectivityManager = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + return connectivityManager != null && connectivityManager.getActiveNetworkInfo() != null + && connectivityManager.getActiveNetworkInfo().isConnectedOrConnecting(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/RootChecker.java b/app/src/main/java/io/github/wulkanowy/utils/RootChecker.java new file mode 100644 index 000000000..9c23c217f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/RootChecker.java @@ -0,0 +1,38 @@ +package io.github.wulkanowy.utils; + +import android.os.Build; + +import java.io.File; + +public final class RootChecker { + + private RootChecker() { + throw new IllegalStateException("Utility class"); + } + + public static boolean isRooted() { + return checkOne() || checkTwo() || checkThree(); + } + + private static boolean checkOne() { + return Build.TAGS != null && Build.TAGS.contains("test-keys"); + } + + private static boolean checkTwo() { + return new File("/system/app/Superuser.apk").exists(); + } + + private static boolean checkThree() { + String[] commands = {"/system/xbin/which su", "/system/bin/which su", "which su"}; + for (String command : commands) { + try { + Runtime.getRuntime().exec(command); + return true; + } catch (Exception e) { + // ignore + } + } + return false; + } + +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/SchedulersProvider.kt b/app/src/main/java/io/github/wulkanowy/utils/SchedulersProvider.kt deleted file mode 100644 index e426d4d5d..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/SchedulersProvider.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.wulkanowy.utils - -import io.reactivex.Scheduler -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers - -open class SchedulersProvider { - - open val mainThread: Scheduler - get() = AndroidSchedulers.mainThread() - - open val backgroundThread: Scheduler - get() = Schedulers.io() -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/SpinnerExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SpinnerExtension.kt deleted file mode 100644 index d9e34e87c..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/SpinnerExtension.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.wulkanowy.utils - -import android.view.View -import android.widget.AdapterView -import android.widget.Spinner - -/** - * @see How to keep onItemSelected from firing off on a newly instantiated Spinner? - */ -inline fun Spinner.setOnItemSelectedListener(crossinline listener: (view: View?) -> Unit) { - onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onNothingSelected(parent: AdapterView<*>?) {} - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onNothingSelected(parent: AdapterView<*>?) {} - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - listener(view) - } - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt deleted file mode 100644 index 886b3d4da..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt +++ /dev/null @@ -1,124 +0,0 @@ -package io.github.wulkanowy.utils - -import org.threeten.bp.DateTimeUtils -import org.threeten.bp.DayOfWeek.FRIDAY -import org.threeten.bp.DayOfWeek.MONDAY -import org.threeten.bp.DayOfWeek.SATURDAY -import org.threeten.bp.DayOfWeek.SUNDAY -import org.threeten.bp.Instant -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDateTime -import org.threeten.bp.Month -import org.threeten.bp.ZoneId -import org.threeten.bp.format.DateTimeFormatter -import org.threeten.bp.format.DateTimeFormatter.ofPattern -import org.threeten.bp.format.TextStyle.FULL_STANDALONE -import org.threeten.bp.temporal.TemporalAdjusters.firstInMonth -import org.threeten.bp.temporal.TemporalAdjusters.next -import org.threeten.bp.temporal.TemporalAdjusters.previous -import java.util.Date -import java.util.Locale - -private const val DATE_PATTERN = "dd.MM.yyyy" - -fun Date.toLocalDate(): LocalDate { - return Instant.ofEpochMilli(this.time).atZone(ZoneId.systemDefault()).toLocalDate() -} - -fun Date.toLocalDateTime(): LocalDateTime { - return Instant.ofEpochMilli(this.time).atZone(ZoneId.systemDefault()).toLocalDateTime() -} - -fun String.toLocalDate(format: String = DATE_PATTERN): LocalDate { - return LocalDate.parse(this, DateTimeFormatter.ofPattern(format)) -} - -fun LocalDate.toFormattedString(format: String = DATE_PATTERN): String = this.format(ofPattern(format)) - -fun LocalDateTime.toFormattedString(format: String = DATE_PATTERN): String = this.format(ofPattern(format)) - -fun LocalDateTime.toDate(): Date = DateTimeUtils.toDate(atZone(ZoneId.systemDefault()).toInstant()) - -/** - * https://github.com/ThreeTen/threetenbp/issues/55 - */ - -fun Month.getFormattedName(): String { - return getDisplayName(FULL_STANDALONE, Locale.getDefault()) - .let { - when (it) { - "stycznia" -> "Styczeń" - "lutego" -> "Luty" - "marca" -> "Marzec" - "kwietnia" -> "Kwiecień" - "maja" -> "Maj" - "czerwca" -> "Czerwiec" - "lipca" -> "Lipiec" - "sierpnia" -> "Sierpień" - "września" -> "Wrzesień" - "października" -> "Październik" - "listopada" -> "Listopad" - "grudnia" -> "Grudzień" - else -> it - } - } -} - -inline val LocalDate.nextSchoolDay: LocalDate - get() { - return when (this.dayOfWeek) { - FRIDAY, SATURDAY, SUNDAY -> this.with(next(MONDAY)) - else -> this.plusDays(1) - } - } - -inline val LocalDate.previousSchoolDay: LocalDate - get() { - return when (this.dayOfWeek) { - SATURDAY, SUNDAY, MONDAY -> this.with(previous(FRIDAY)) - else -> this.minusDays(1) - } - } - -inline val LocalDate.nextOrSameSchoolDay: LocalDate - get() { - return when (this.dayOfWeek) { - SATURDAY, SUNDAY -> this.with(next(MONDAY)) - else -> this - } - } - -inline val LocalDate.previousOrSameSchoolDay: LocalDate - get() { - return when (this.dayOfWeek) { - SATURDAY, SUNDAY -> this.with(previous(FRIDAY)) - else -> this - } - } - -inline val LocalDate.weekDayName: String - get() = this.format(ofPattern("EEEE", Locale.getDefault())) - -inline val LocalDate.monday: LocalDate - get() = this.with(MONDAY) - -inline val LocalDate.friday: LocalDate - get() = this.with(FRIDAY) - -/** - * [Dz.U. 2016 poz. 1335](http://prawo.sejm.gov.pl/isap.nsf/DocDetails.xsp?id=WDU20160001335) - */ - -inline val LocalDate.isHolidays: Boolean - get() { - return LocalDate.of(this.year, 9, 1).run { - when (dayOfWeek) { - FRIDAY, SATURDAY, SUNDAY -> with(firstInMonth(MONDAY)) - else -> this - } - }.let { firstSchoolDay -> - LocalDate.of(this.year, 6, 20) - .with(next(FRIDAY)) - .let { lastSchoolDay -> this.isBefore(firstSchoolDay) && this.isAfter(lastSchoolDay) } - } - } diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.java b/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.java new file mode 100644 index 000000000..717ed42a9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.java @@ -0,0 +1,89 @@ +package io.github.wulkanowy.utils; + +import org.joda.time.DateTime; +import org.joda.time.DateTimeConstants; +import org.joda.time.LocalDate; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + +import static io.github.wulkanowy.utils.AppConstant.DATE_PATTERN; + +public final class TimeUtils { + + private static final long TICKS_AT_EPOCH = 621355968000000000L; + + private static final long TICKS_PER_MILLISECOND = 10000; + + private TimeUtils() { + throw new IllegalStateException("Utility class"); + } + + public static long getNetTicks(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + + return (calendar.getTimeInMillis() * TICKS_PER_MILLISECOND) + TICKS_AT_EPOCH; + } + + public static long getNetTicks(String dateString) throws ParseException { + return getNetTicks(dateString, DATE_PATTERN); + } + + public static long getNetTicks(String dateString, String dateFormat) throws ParseException { + SimpleDateFormat format = new SimpleDateFormat(dateFormat, Locale.ROOT); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + Date dateObject = format.parse(dateString); + + return getNetTicks(dateObject); + } + + public static Date getDate(long netTicks) { + return new Date((netTicks - TICKS_AT_EPOCH) / TICKS_PER_MILLISECOND); + } + + public static List getMondaysFromCurrentSchoolYear() { + LocalDate startDate = new LocalDate(getCurrentSchoolYear(), 9, 1); + LocalDate endDate = new LocalDate(getCurrentSchoolYear() + 1, 8, 31); + + List dateList = new ArrayList<>(); + + LocalDate thisMonday = startDate.withDayOfWeek(DateTimeConstants.MONDAY); + + if (startDate.isAfter(thisMonday)) { + startDate = thisMonday.plusWeeks(1); + } else { + startDate = thisMonday; + } + + while (startDate.isBefore(endDate)) { + dateList.add(startDate.toString(DATE_PATTERN)); + startDate = startDate.plusWeeks(1); + } + return dateList; + } + + public static int getCurrentSchoolYear() { + DateTime dateTime = new DateTime(); + return dateTime.getMonthOfYear() <= 8 ? dateTime.getYear() - 1 : dateTime.getYear(); + } + + public static String getDateOfCurrentMonday(boolean normalize) { + DateTime currentDate = new DateTime(); + + if (currentDate.getDayOfWeek() == DateTimeConstants.SATURDAY && normalize) { + currentDate = currentDate.plusDays(2); + } else if (currentDate.getDayOfWeek() == DateTimeConstants.SUNDAY && normalize) { + currentDate = currentDate.plusDays(1); + } else { + currentDate = currentDate.withDayOfWeek(DateTimeConstants.MONDAY); + } + return currentDate.toString(DATE_PATTERN); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt deleted file mode 100644 index 6a5ad880a..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.utils - -import androidx.viewpager.widget.ViewPager - -inline fun ViewPager.setOnSelectPageListener(crossinline selectListener: (position: Int) -> Unit) { - addOnPageChangeListener(object : ViewPager.OnPageChangeListener { - override fun onPageSelected(position: Int) { - selectListener(position) - } - override fun onPageScrollStateChanged(state: Int) {} - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} - }) -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java b/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java new file mode 100644 index 000000000..5106d6417 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java @@ -0,0 +1,63 @@ +package io.github.wulkanowy.utils.async; + +import android.os.AsyncTask; + +import io.github.wulkanowy.utils.LogUtils; + +public class AbstractTask extends AsyncTask { + + private Exception exception; + + private AsyncListeners.OnRefreshListener onRefreshListener; + + private AsyncListeners.OnFirstLoadingListener onFirstLoadingListener; + + public void setOnFirstLoadingListener(AsyncListeners.OnFirstLoadingListener onFirstLoadingListener) { + this.onFirstLoadingListener = onFirstLoadingListener; + } + + public void setOnRefreshListener(AsyncListeners.OnRefreshListener onRefreshListener) { + this.onRefreshListener = onRefreshListener; + } + + @Override + protected Boolean doInBackground(Void... voids) { + try { + if (onFirstLoadingListener != null) { + onFirstLoadingListener.onDoInBackgroundLoading(); + } else if (onRefreshListener != null) { + onRefreshListener.onDoInBackgroundRefresh(); + } else { + LogUtils.error("AbstractTask does not have a listener assigned"); + } + return true; + } catch (Exception e) { + exception = e; + return false; + } + } + + @Override + protected void onCancelled() { + super.onCancelled(); + if (onFirstLoadingListener != null) { + onFirstLoadingListener.onCanceledLoadingAsync(); + } else if (onRefreshListener != null) { + onRefreshListener.onCanceledRefreshAsync(); + } else { + LogUtils.error("AbstractTask does not have a listener assigned"); + } + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (onFirstLoadingListener != null) { + onFirstLoadingListener.onEndLoadingAsync(result, exception); + } else if (onRefreshListener != null) { + onRefreshListener.onEndRefreshAsync(result, exception); + } else { + LogUtils.error("AbstractTask does not have a listener assigned"); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/async/AsyncListeners.java b/app/src/main/java/io/github/wulkanowy/utils/async/AsyncListeners.java new file mode 100644 index 000000000..0d33c57cf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/async/AsyncListeners.java @@ -0,0 +1,23 @@ +package io.github.wulkanowy.utils.async; + +public interface AsyncListeners { + + interface OnRefreshListener { + + void onDoInBackgroundRefresh() throws Exception; + + void onCanceledRefreshAsync(); + + void onEndRefreshAsync(boolean result, Exception exception); + + } + + interface OnFirstLoadingListener { + + void onDoInBackgroundLoading() throws Exception; + + void onCanceledLoadingAsync(); + + void onEndLoadingAsync(boolean result, Exception exception); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/CryptoException.java b/app/src/main/java/io/github/wulkanowy/utils/security/CryptoException.java new file mode 100644 index 000000000..1ee4a9fa8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/security/CryptoException.java @@ -0,0 +1,9 @@ +package io.github.wulkanowy.utils.security; + + +public class CryptoException extends Exception { + + public CryptoException(String message) { + super(message); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java new file mode 100644 index 000000000..93da1d0bd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java @@ -0,0 +1,184 @@ +package io.github.wulkanowy.utils.security; + + +import android.annotation.TargetApi; +import android.content.Context; +import android.os.Build; +import android.security.KeyPairGeneratorSpec; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.util.Base64; + +import org.apache.commons.lang3.ArrayUtils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.math.BigInteger; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.util.ArrayList; +import java.util.Calendar; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.security.auth.x500.X500Principal; + +import io.github.wulkanowy.utils.LogUtils; +import io.github.wulkanowy.utils.RootChecker; + +public final class Scrambler { + + private static final String ANDROID_KEYSTORE = "AndroidKeyStore"; + + private static KeyStore keyStore; + + private Scrambler() { + throw new IllegalStateException("Utility class"); + } + + public static String encrypt(String email, String plainText, Context context) + throws CryptoException { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + loadKeyStore(); + generateNewKey(email, context); + return encryptString(email, plainText); + } else { + if (RootChecker.isRooted()) { + return new String(Base64.encode(plainText.getBytes(), Base64.DEFAULT)); + } else { + throw new UnsupportedOperationException("Stored data in this devices " + + "isn't safe because android is rooted"); + } + } + } + + public static String decrypt(String email, String encryptedText) throws CryptoException { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + loadKeyStore(); + return decryptString(email, encryptedText); + } else { + return new String(Base64.decode(encryptedText, Base64.DEFAULT)); + } + } + + private static void loadKeyStore() throws CryptoException { + try { + keyStore = KeyStore.getInstance(ANDROID_KEYSTORE); + keyStore.load(null); + } catch (Exception e) { + throw new CryptoException(e.getMessage()); + } + } + + @SuppressWarnings("deprecation") + @TargetApi(18) + private static void generateNewKey(String alias, Context context) throws CryptoException { + + Calendar start = Calendar.getInstance(); + Calendar end = Calendar.getInstance(); + + AlgorithmParameterSpec spec; + + end.add(Calendar.YEAR, 10); + if (!"".equals(alias)) { + try { + if (!keyStore.containsAlias(alias)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + spec = new KeyGenParameterSpec.Builder(alias, + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) + .setDigests(KeyProperties.DIGEST_SHA256) + .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) + .setCertificateNotBefore(start.getTime()) + .setCertificateNotAfter(end.getTime()) + .build(); + + } else { + spec = new KeyPairGeneratorSpec.Builder(context) + .setAlias(alias) + .setSubject(new X500Principal("CN=" + alias)) + .setSerialNumber(BigInteger.TEN) + .setStartDate(start.getTime()) + .setEndDate(end.getTime()) + .build(); + } + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", + ANDROID_KEYSTORE); + keyPairGenerator.initialize(spec); + keyPairGenerator.generateKeyPair(); + } + } catch (Exception e) { + throw new CryptoException(e.getMessage()); + } + } else { + throw new CryptoException("GenerateNewKey - String is empty"); + } + + LogUtils.debug("Key pair are create"); + + } + + private static String encryptString(String alias, String text) throws CryptoException { + + if (!alias.isEmpty() && !text.isEmpty()) { + try { + KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null); + RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey(); + + Cipher input = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + input.init(Cipher.ENCRYPT_MODE, publicKey); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + CipherOutputStream cipherOutputStream = new CipherOutputStream( + outputStream, input); + cipherOutputStream.write(text.getBytes("UTF-8")); + cipherOutputStream.close(); + + byte[] values = outputStream.toByteArray(); + + return Base64.encodeToString(values, Base64.DEFAULT); + + } catch (Exception e) { + throw new CryptoException(e.getMessage()); + } + } else { + throw new CryptoException("EncryptString - String is empty"); + } + } + + private static String decryptString(String alias, String text) throws CryptoException { + + if (!alias.isEmpty() && !text.isEmpty()) { + try { + KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, null); + + Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey()); + + CipherInputStream cipherInputStream = new CipherInputStream( + new ByteArrayInputStream(Base64.decode(text, Base64.DEFAULT)), output); + + ArrayList values = new ArrayList<>(); + + int nextByte; + + while ((nextByte = cipherInputStream.read()) != -1) { + values.add((byte) nextByte); + } + + Byte[] bytes = values.toArray(new Byte[values.size()]); + + return new String(ArrayUtils.toPrimitive(bytes), 0, bytes.length, "UTF-8"); + } catch (Exception e) { + throw new CryptoException(e.getMessage()); + } + } else { + throw new CryptoException("EncryptString - String is empty"); + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt deleted file mode 100644 index 24e6d3ffa..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt +++ /dev/null @@ -1,149 +0,0 @@ -@file:Suppress("DEPRECATION") - -package io.github.wulkanowy.utils.security - -import android.annotation.TargetApi -import android.content.Context -import android.os.Build.VERSION.SDK_INT -import android.os.Build.VERSION_CODES.JELLY_BEAN_MR2 -import android.os.Build.VERSION_CODES.M -import android.security.KeyPairGeneratorSpec -import android.security.keystore.KeyGenParameterSpec -import android.security.keystore.KeyProperties.DIGEST_SHA256 -import android.security.keystore.KeyProperties.DIGEST_SHA512 -import android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_OAEP -import android.security.keystore.KeyProperties.PURPOSE_DECRYPT -import android.security.keystore.KeyProperties.PURPOSE_ENCRYPT -import android.util.Base64 -import android.util.Base64.DEFAULT -import android.util.Base64.decode -import android.util.Base64.encode -import android.util.Base64.encodeToString -import timber.log.Timber -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.math.BigInteger -import java.nio.charset.Charset -import java.security.KeyPairGenerator -import java.security.KeyStore -import java.security.spec.MGF1ParameterSpec.SHA1 -import java.util.Calendar -import java.util.Calendar.YEAR -import javax.crypto.Cipher -import javax.crypto.Cipher.DECRYPT_MODE -import javax.crypto.Cipher.ENCRYPT_MODE -import javax.crypto.CipherInputStream -import javax.crypto.CipherOutputStream -import javax.crypto.spec.OAEPParameterSpec -import javax.crypto.spec.PSource.PSpecified -import javax.security.auth.x500.X500Principal - -private const val KEYSTORE_NAME = "AndroidKeyStore" - -private const val KEY_ALIAS = "wulkanowy_password" - -private val KEY_CHARSET = Charset.forName("UTF-8") - -private val isKeyPairExists: Boolean - get() = keyStore.getKey(KEY_ALIAS, null) != null - -private val keyStore: KeyStore - get() = KeyStore.getInstance(KEYSTORE_NAME).apply { load(null) } - -private val cipher: Cipher - get() { - return if (SDK_INT >= M) Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "AndroidKeyStoreBCWorkaround") - else Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL") - } - -fun encrypt(plainText: String, context: Context): String { - if (plainText.isEmpty()) throw ScramblerException("Text to be encrypted is empty") - - if (SDK_INT < JELLY_BEAN_MR2) { - return String(Base64.encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET) - } - - return try { - if (!isKeyPairExists) generateKeyPair(context) - - cipher.let { - if (SDK_INT >= M) { - OAEPParameterSpec("SHA-256", "MGF1", SHA1, PSpecified.DEFAULT).let { spec -> - it.init(ENCRYPT_MODE, keyStore.getCertificate(KEY_ALIAS).publicKey, spec) - } - } else it.init(ENCRYPT_MODE, keyStore.getCertificate(KEY_ALIAS).publicKey) - - ByteArrayOutputStream().let { output -> - CipherOutputStream(output, it).apply { - write(plainText.toByteArray(KEY_CHARSET)) - close() - } - encodeToString(output.toByteArray(), DEFAULT) - } - } - } catch (exception: Exception) { - Timber.e(exception, "An error occurred while encrypting text") - String(encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET) - } -} - -fun decrypt(cipherText: String): String { - if (cipherText.isEmpty()) throw ScramblerException("Text to be encrypted is empty") - - return try { - if (SDK_INT < JELLY_BEAN_MR2 || cipherText.length < 250) { - return String(decode(cipherText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET) - } - - if (!isKeyPairExists) throw ScramblerException("KeyPair doesn't exist") - - cipher.let { - if (SDK_INT >= M) { - OAEPParameterSpec("SHA-256", "MGF1", SHA1, PSpecified.DEFAULT).let { spec -> - it.init(DECRYPT_MODE, keyStore.getKey(KEY_ALIAS, null), spec) - } - } else it.init(DECRYPT_MODE, keyStore.getKey(KEY_ALIAS, null)) - - CipherInputStream(ByteArrayInputStream(decode(cipherText, DEFAULT)), it).let { input -> - val values = ArrayList() - var nextByte = 0 - while ({ nextByte = input.read(); nextByte }() != -1) { - values.add(nextByte.toByte()) - } - val bytes = ByteArray(values.size) - for (i in bytes.indices) { - bytes[i] = values[i] - } - String(bytes, 0, bytes.size, KEY_CHARSET) - } - } - } catch (e: Exception) { - throw ScramblerException("An error occurred while decrypting text", e) - } -} - -@TargetApi(JELLY_BEAN_MR2) -private fun generateKeyPair(context: Context) { - (if (SDK_INT >= M) { - KeyGenParameterSpec.Builder(KEY_ALIAS, PURPOSE_DECRYPT or PURPOSE_ENCRYPT) - .setDigests(DIGEST_SHA256, DIGEST_SHA512) - .setEncryptionPaddings(ENCRYPTION_PADDING_RSA_OAEP) - .setCertificateSerialNumber(BigInteger.TEN) - .setCertificateSubject(X500Principal("CN=Wulkanowy")) - .build() - } else { - KeyPairGeneratorSpec.Builder(context) - .setAlias(KEY_ALIAS) - .setSubject(X500Principal("CN=Wulkanowy")) - .setSerialNumber(BigInteger.TEN) - .setStartDate(Calendar.getInstance().time) - .setEndDate(Calendar.getInstance().apply { add(YEAR, 99) }.time) - .build() - }).let { - KeyPairGenerator.getInstance("RSA", KEYSTORE_NAME).apply { - initialize(it) - genKeyPair() - } - } - Timber.i("A new KeyPair has been generated") -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/ScramblerException.kt b/app/src/main/java/io/github/wulkanowy/utils/security/ScramblerException.kt deleted file mode 100644 index 59f830fad..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/security/ScramblerException.kt +++ /dev/null @@ -1,6 +0,0 @@ -package io.github.wulkanowy.utils.security - -class ScramblerException : Exception { - constructor(message: String, cause: Throwable) : super(message, cause) - constructor(message: String) : super(message) -} diff --git a/app/src/main/play/contact-email.txt b/app/src/main/play/contact-email.txt deleted file mode 100644 index 851521eb7..000000000 --- a/app/src/main/play/contact-email.txt +++ /dev/null @@ -1 +0,0 @@ -wulkanowyinc@gmail.com diff --git a/app/src/main/play/contact-phone.txt b/app/src/main/play/contact-phone.txt deleted file mode 100644 index 552e53a58..000000000 --- a/app/src/main/play/contact-phone.txt +++ /dev/null @@ -1 +0,0 @@ -+48733393622 diff --git a/app/src/main/play/contact-website.txt b/app/src/main/play/contact-website.txt deleted file mode 100644 index 53f7bee6b..000000000 --- a/app/src/main/play/contact-website.txt +++ /dev/null @@ -1 +0,0 @@ -https://wulkanowy.github.io/ diff --git a/app/src/main/play/default-language.txt b/app/src/main/play/default-language.txt deleted file mode 100644 index 1134c4d5c..000000000 --- a/app/src/main/play/default-language.txt +++ /dev/null @@ -1 +0,0 @@ -pl-PL diff --git a/app/src/main/play/listings/pl-PL/full-description.txt b/app/src/main/play/listings/pl-PL/full-description.txt deleted file mode 100644 index d33cb56f2..000000000 --- a/app/src/main/play/listings/pl-PL/full-description.txt +++ /dev/null @@ -1,14 +0,0 @@ -Aplikacja jest we wczesnej fazie rozwoju, ciągle pracujemy nad kolejnymi funkcjami. - -Wyróżnione cechy i funkcje: -- obliczanie średniej ważonej, -- procentowy podgląd frekwencji, -- szczęśliwy numerek, -- podgląd lekcji zrealizowanych, -- ciemny motyw. -- brak reklam, -- tryb offline, -- powiadomienia. - -GitHub: https://github.com/wulkanowy/wulkanowy -Discord: https://discord.gg/vccAQBr diff --git a/app/src/main/play/listings/pl-PL/graphics/feature-graphic/feature.png b/app/src/main/play/listings/pl-PL/graphics/feature-graphic/feature.png deleted file mode 100644 index 094b25b6c..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/feature-graphic/feature.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/icon/icon.png b/app/src/main/play/listings/pl-PL/graphics/icon/icon.png deleted file mode 100644 index 8c33c2f92..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/icon/icon.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/account-switcher.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/account-switcher.png deleted file mode 100644 index 8a52d8de8..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/account-switcher.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-dialog.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-dialog.png deleted file mode 100644 index c65085747..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-dialog.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-statistics.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-statistics.png deleted file mode 100644 index 376fc3c3f..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-statistics.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/grades.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/grades.png deleted file mode 100644 index 6b4089b02..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/grades.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/more.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/more.png deleted file mode 100644 index c3dc523ff..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/more.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-dialog.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-dialog.png deleted file mode 100644 index 511433d45..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-dialog.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-widget.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-widget.png deleted file mode 100644 index 1d5024707..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-widget.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/short-description.txt b/app/src/main/play/listings/pl-PL/short-description.txt deleted file mode 100644 index c850c159a..000000000 --- a/app/src/main/play/listings/pl-PL/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -Nieoficjalna aplikacja ucznia i rodzica dla dziennika VULCAN UONET+ diff --git a/app/src/main/play/listings/pl-PL/title.txt b/app/src/main/play/listings/pl-PL/title.txt deleted file mode 100644 index 2d53a62a8..000000000 --- a/app/src/main/play/listings/pl-PL/title.txt +++ /dev/null @@ -1 +0,0 @@ -Wulkanowy Dzienniczek diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt deleted file mode 100644 index c85a155b7..000000000 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ /dev/null @@ -1,11 +0,0 @@ -Wersja 0.7.4 - -Naprawiliśmy: -- problem ze stabilnością na androidach 4 i 5 -- problem z przełączaniem kont, jeśli zalogowany był jednocześnie uczeń i rodzic -- problem z odświeżaniem danych, jeśli uczeń przeniósł się z klasy do klasy w bieżącym roku szkolnym - -Dodaliśmy: -- nową opcję zmiany wartości plusa i minusa na 0.75 - -Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.4 diff --git a/app/src/main/res/drawable-anydpi-v24/ic_stat_notify_lucky_number.xml b/app/src/main/res/drawable-anydpi-v24/ic_stat_notify_lucky_number.xml deleted file mode 100644 index 00538168f..000000000 --- a/app/src/main/res/drawable-anydpi-v24/ic_stat_notify_lucky_number.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable-hdpi/ic_stat_notify.png b/app/src/main/res/drawable-hdpi/ic_stat_notify.png new file mode 100644 index 000000000..34544980a Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_stat_notify.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_stat_notify_grade.png b/app/src/main/res/drawable-hdpi/ic_stat_notify_grade.png deleted file mode 100644 index ea5b85c82..000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_stat_notify_grade.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_stat_notify_lucky_number.png b/app/src/main/res/drawable-hdpi/ic_stat_notify_lucky_number.png deleted file mode 100644 index b91c4ae6c..000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_stat_notify_lucky_number.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_stat_notify_message.png b/app/src/main/res/drawable-hdpi/ic_stat_notify_message.png deleted file mode 100644 index 86d63c587..000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_stat_notify_message.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_stat_notify_note.png b/app/src/main/res/drawable-hdpi/ic_stat_notify_note.png deleted file mode 100644 index b49e4ad2c..000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_stat_notify_note.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_stat_notify.png b/app/src/main/res/drawable-mdpi/ic_stat_notify.png new file mode 100644 index 000000000..5564049af Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_stat_notify.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_stat_notify_grade.png b/app/src/main/res/drawable-mdpi/ic_stat_notify_grade.png deleted file mode 100644 index 64fd285d6..000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_stat_notify_grade.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_stat_notify_lucky_number.png b/app/src/main/res/drawable-mdpi/ic_stat_notify_lucky_number.png deleted file mode 100644 index bfced4eb0..000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_stat_notify_lucky_number.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_stat_notify_message.png b/app/src/main/res/drawable-mdpi/ic_stat_notify_message.png deleted file mode 100644 index 73a1653a0..000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_stat_notify_message.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_stat_notify_note.png b/app/src/main/res/drawable-mdpi/ic_stat_notify_note.png deleted file mode 100644 index 3498f71c3..000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_stat_notify_note.png and /dev/null differ diff --git a/app/src/main/res/drawable-night/ic_all_divider.xml b/app/src/main/res/drawable-night/ic_all_divider.xml deleted file mode 100644 index ca44eb29e..000000000 --- a/app/src/main/res/drawable-night/ic_all_divider.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/layer_splash_background.xml b/app/src/main/res/drawable-v15/splash_background.xml similarity index 51% rename from app/src/main/res/drawable/layer_splash_background.xml rename to app/src/main/res/drawable-v15/splash_background.xml index 64812f151..c58c374a1 100644 --- a/app/src/main/res/drawable/layer_splash_background.xml +++ b/app/src/main/res/drawable-v15/splash_background.xml @@ -1,12 +1,14 @@ + - + + + android:gravity="center" + android:src="@mipmap/ic_launcher"/> diff --git a/app/src/main/res/drawable-v23/img_splash_logo.png b/app/src/main/res/drawable-v23/img_splash_logo.png deleted file mode 100644 index 61489d81b..000000000 Binary files a/app/src/main/res/drawable-v23/img_splash_logo.png and /dev/null differ diff --git a/app/src/main/res/drawable-v23/layer_splash_background.xml b/app/src/main/res/drawable-v23/layer_splash_background.xml deleted file mode 100644 index 5fa58e900..000000000 --- a/app/src/main/res/drawable-v23/layer_splash_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable-v23/splash_background.xml b/app/src/main/res/drawable-v23/splash_background.xml new file mode 100644 index 000000000..611bb2a7a --- /dev/null +++ b/app/src/main/res/drawable-v23/splash_background.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_notify.png b/app/src/main/res/drawable-xhdpi/ic_stat_notify.png new file mode 100644 index 000000000..fabb8760c Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_stat_notify.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_notify_grade.png b/app/src/main/res/drawable-xhdpi/ic_stat_notify_grade.png deleted file mode 100644 index 83b2c443a..000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_stat_notify_grade.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_notify_lucky_number.png b/app/src/main/res/drawable-xhdpi/ic_stat_notify_lucky_number.png deleted file mode 100644 index 49e502ac0..000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_stat_notify_lucky_number.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_notify_message.png b/app/src/main/res/drawable-xhdpi/ic_stat_notify_message.png deleted file mode 100644 index b373ffd4a..000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_stat_notify_message.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_notify_note.png b/app/src/main/res/drawable-xhdpi/ic_stat_notify_note.png deleted file mode 100644 index 5aa30e5f7..000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_stat_notify_note.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat_notify.png b/app/src/main/res/drawable-xxhdpi/ic_stat_notify.png new file mode 100644 index 000000000..c1d78d465 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_stat_notify.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_grade.png b/app/src/main/res/drawable-xxhdpi/ic_stat_notify_grade.png deleted file mode 100644 index cba48ec20..000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_grade.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_lucky_number.png b/app/src/main/res/drawable-xxhdpi/ic_stat_notify_lucky_number.png deleted file mode 100644 index 9bab13731..000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_lucky_number.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_message.png b/app/src/main/res/drawable-xxhdpi/ic_stat_notify_message.png deleted file mode 100644 index dc15bbd96..000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_message.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_note.png b/app/src/main/res/drawable-xxhdpi/ic_stat_notify_note.png deleted file mode 100644 index db3ec517b..000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_note.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_stat_notify.png b/app/src/main/res/drawable-xxxhdpi/ic_stat_notify.png new file mode 100644 index 000000000..ee89e6015 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_stat_notify.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_message.png b/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_message.png deleted file mode 100644 index 3b40ad3f1..000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_message.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_note.png b/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_note.png deleted file mode 100644 index 06a9299a9..000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_note.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_account_circular_border.xml b/app/src/main/res/drawable/ic_account_circular_border.xml deleted file mode 100644 index 8ba66975a..000000000 --- a/app/src/main/res/drawable/ic_account_circular_border.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_all_round_note_24dp.xml b/app/src/main/res/drawable/ic_alert_24dp.xml similarity index 97% rename from app/src/main/res/drawable/ic_all_round_note_24dp.xml rename to app/src/main/res/drawable/ic_alert_24dp.xml index f4f1f2c52..1c41b556d 100644 --- a/app/src/main/res/drawable/ic_all_round_note_24dp.xml +++ b/app/src/main/res/drawable/ic_alert_24dp.xml @@ -7,4 +7,4 @@ android:fillColor="@color/colorPrimary" android:pathData="M13 13h-2V7h2m0 10h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z" /> - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_all_about_24dp.xml b/app/src/main/res/drawable/ic_all_about_24dp.xml deleted file mode 100644 index 550f312e8..000000000 --- a/app/src/main/res/drawable/ic_all_about_24dp.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_all_account_24dp.xml b/app/src/main/res/drawable/ic_all_account_24dp.xml deleted file mode 100644 index dea10cb34..000000000 --- a/app/src/main/res/drawable/ic_all_account_24dp.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_all_close_circle_24dp.xml b/app/src/main/res/drawable/ic_all_close_circle_24dp.xml deleted file mode 100644 index 3809c7979..000000000 --- a/app/src/main/res/drawable/ic_all_close_circle_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_all_divider.xml b/app/src/main/res/drawable/ic_border.xml similarity index 97% rename from app/src/main/res/drawable/ic_all_divider.xml rename to app/src/main/res/drawable/ic_border.xml index f46b3ad71..dc33b8aa2 100644 --- a/app/src/main/res/drawable/ic_all_divider.xml +++ b/app/src/main/res/drawable/ic_border.xml @@ -6,4 +6,4 @@ android:centerColor="@android:color/transparent" android:centerX="0.01" android:startColor="#60606060" /> - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_all_note_24dp.xml b/app/src/main/res/drawable/ic_exclamation_24dp.xml similarity index 87% rename from app/src/main/res/drawable/ic_all_note_24dp.xml rename to app/src/main/res/drawable/ic_exclamation_24dp.xml index 988df22c0..4d3c91cfb 100644 --- a/app/src/main/res/drawable/ic_all_note_24dp.xml +++ b/app/src/main/res/drawable/ic_exclamation_24dp.xml @@ -1,4 +1,4 @@ - + - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index cfec6390b..c5ecc7474 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,27 +1,17 @@ - - - - - - - + android:width="108dp" + android:height="108dp" + android:viewportHeight="51.67464" + android:viewportWidth="51.67464"> + + diff --git a/app/src/main/res/drawable/ic_login_outlined_border.xml b/app/src/main/res/drawable/ic_login_outlined_border.xml deleted file mode 100644 index 359bea5e4..000000000 --- a/app/src/main/res/drawable/ic_login_outlined_border.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_menu_main_attendance_24dp.xml b/app/src/main/res/drawable/ic_menu_attendance_24dp.xml similarity index 59% rename from app/src/main/res/drawable/ic_menu_main_attendance_24dp.xml rename to app/src/main/res/drawable/ic_menu_attendance_24dp.xml index eccc338fc..fdef73225 100644 --- a/app/src/main/res/drawable/ic_menu_main_attendance_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_attendance_24dp.xml @@ -5,7 +5,8 @@ android:viewportHeight="24" android:viewportWidth="24"> - + android:fillColor="#ffffff" + android:pathData="M22 13l-1 1-2-2 1-1 2 2m-10 6l6-6 2 2-6 + 6h-2v-2M4 2h14a2 2 0 0 1 2 2v4l-4 4h-4v4l-2 2H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2m0 4v4h6V6H4m8 + 0v4h6V6h-6m-8 6v4h6v-4H4z" /> + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_menu_dashboard_24dp.xml b/app/src/main/res/drawable/ic_menu_dashboard_24dp.xml new file mode 100644 index 000000000..50726abd1 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_dashboard_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_main_grade_26dp.xml b/app/src/main/res/drawable/ic_menu_grade_26dp.xml similarity index 91% rename from app/src/main/res/drawable/ic_menu_main_grade_26dp.xml rename to app/src/main/res/drawable/ic_menu_grade_26dp.xml index 3866cd3c7..61506dada 100644 --- a/app/src/main/res/drawable/ic_menu_main_grade_26dp.xml +++ b/app/src/main/res/drawable/ic_menu_grade_26dp.xml @@ -4,8 +4,8 @@ android:viewportHeight="26" android:viewportWidth="26"> - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_menu_grade_semester_24dp.xml b/app/src/main/res/drawable/ic_menu_grade_semester_24dp.xml deleted file mode 100644 index 2a03f6300..000000000 --- a/app/src/main/res/drawable/ic_menu_grade_semester_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_main_attendance_summary_24dp.xml b/app/src/main/res/drawable/ic_menu_main_attendance_summary_24dp.xml deleted file mode 100644 index 13cfc8fad..000000000 --- a/app/src/main/res/drawable/ic_menu_main_attendance_summary_24dp.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_main_exam_24dp.xml b/app/src/main/res/drawable/ic_menu_main_exam_24dp.xml deleted file mode 100644 index 13179b7c3..000000000 --- a/app/src/main/res/drawable/ic_menu_main_exam_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_main_homework_24dp.xml b/app/src/main/res/drawable/ic_menu_main_homework_24dp.xml deleted file mode 100644 index 449f0e28a..000000000 --- a/app/src/main/res/drawable/ic_menu_main_homework_24dp.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_menu_main_lessons_completed_24dp.xml b/app/src/main/res/drawable/ic_menu_main_lessons_completed_24dp.xml deleted file mode 100644 index c0664c370..000000000 --- a/app/src/main/res/drawable/ic_menu_main_lessons_completed_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_main_more_24dp.xml b/app/src/main/res/drawable/ic_menu_main_more_24dp.xml deleted file mode 100644 index 26932b2b2..000000000 --- a/app/src/main/res/drawable/ic_menu_main_more_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_main_note_24dp.xml b/app/src/main/res/drawable/ic_menu_main_note_24dp.xml deleted file mode 100644 index 6b3172baf..000000000 --- a/app/src/main/res/drawable/ic_menu_main_note_24dp.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_menu_main_timetable_24dp.xml b/app/src/main/res/drawable/ic_menu_main_timetable_24dp.xml deleted file mode 100644 index 1594285f9..000000000 --- a/app/src/main/res/drawable/ic_menu_main_timetable_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_other_24dp.xml b/app/src/main/res/drawable/ic_menu_other_24dp.xml new file mode 100644 index 000000000..9fed586b0 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_other_24dp.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_menu_send_message_24dp.xml b/app/src/main/res/drawable/ic_menu_send_message_24dp.xml deleted file mode 100644 index fcc5acfb0..000000000 --- a/app/src/main/res/drawable/ic_menu_send_message_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_timetable_24dp.xml b/app/src/main/res/drawable/ic_menu_timetable_24dp.xml new file mode 100644 index 000000000..a689dc064 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_timetable_24dp.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_message_reply_24dp.xml b/app/src/main/res/drawable/ic_message_reply_24dp.xml deleted file mode 100644 index 010cb250b..000000000 --- a/app/src/main/res/drawable/ic_message_reply_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_more_lucky_number_24dp.xml b/app/src/main/res/drawable/ic_more_lucky_number_24dp.xml deleted file mode 100644 index f2e6152d8..000000000 --- a/app/src/main/res/drawable/ic_more_lucky_number_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_more_messages_24dp.xml b/app/src/main/res/drawable/ic_more_messages_24dp.xml deleted file mode 100644 index 65f32169d..000000000 --- a/app/src/main/res/drawable/ic_more_messages_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_more_settings_24dp.xml b/app/src/main/res/drawable/ic_more_settings_24dp.xml deleted file mode 100644 index fc12cdcfe..000000000 --- a/app/src/main/res/drawable/ic_more_settings_24dp.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_send_message_24dp.xml b/app/src/main/res/drawable/ic_send_message_24dp.xml deleted file mode 100644 index 0d82394be..000000000 --- a/app/src/main/res/drawable/ic_send_message_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_timetable_swap_30dp.xml b/app/src/main/res/drawable/ic_swap_30dp.xml similarity index 100% rename from app/src/main/res/drawable/ic_timetable_swap_30dp.xml rename to app/src/main/res/drawable/ic_swap_30dp.xml diff --git a/app/src/main/res/drawable/ic_widget_chevron_24dp.png b/app/src/main/res/drawable/ic_widget_chevron_24dp.png deleted file mode 100644 index 34345521a..000000000 Binary files a/app/src/main/res/drawable/ic_widget_chevron_24dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_wrench_24dp.xml b/app/src/main/res/drawable/ic_wrench_24dp.xml new file mode 100644 index 000000000..8f3a86faa --- /dev/null +++ b/app/src/main/res/drawable/ic_wrench_24dp.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/img_splash_512px.png b/app/src/main/res/drawable/img_splash_512px.png new file mode 100644 index 000000000..9d8f403c2 Binary files /dev/null and b/app/src/main/res/drawable/img_splash_512px.png differ diff --git a/app/src/main/res/drawable/img_splash_logo.png b/app/src/main/res/drawable/img_splash_logo.png deleted file mode 100644 index fb521bf65..000000000 Binary files a/app/src/main/res/drawable/img_splash_logo.png and /dev/null differ diff --git a/app/src/main/res/drawable/img_timetable_widget_preview.png b/app/src/main/res/drawable/img_timetable_widget_preview.png deleted file mode 100644 index 60a35ab6a..000000000 Binary files a/app/src/main/res/drawable/img_timetable_widget_preview.png and /dev/null differ diff --git a/app/src/main/res/drawable/splash_background.xml b/app/src/main/res/drawable/splash_background.xml new file mode 100644 index 000000000..0b79b6729 --- /dev/null +++ b/app/src/main/res/drawable/splash_background.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 1a86fa925..670959826 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -1,10 +1,138 @@ - + android:layout_height="match_parent" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + tools:context="io.github.wulkanowy.ui.login.LoginActivity"> - + - + android:layout_height="match_parent" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:layout_alignParentTop="true" + android:gravity="center" + android:visibility="gone"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +