diff --git a/.bettercodehub.yml b/.bettercodehub.yml index 349f7675..cce850d7 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 1ae402f5..841ff9e0 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 publishRelease --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 27d57f59..00000000 --- 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 6bc531b6..3b524b65 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 0d1bcd5d..d8fdd058 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 00000000..f5bd97e6 --- /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 eb4efba7..00000000 --- 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 0f7bc519..00000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 7ac24c77..48777522 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 a430c90e..00000000 --- a/.travis.yml +++ /dev/null @@ -1,63 +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/ - -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 build -x test -x lint -x fabricGenerateResourcesRelease -x packageRelease --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 publishRelease -PenableCrashlytics --stacktrace; - fi - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/README.md b/README.md index ccbf14ca..76a8d6e6 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) +[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) +[![Dependency Status](https://www.versioneye.com/user/projects/5969ff0b0fb24f004f8c711b/badge.svg?style=flat-square)](https://www.versioneye.com/user/projects/5969ff0b0fb24f004f8c711b) +[![CircleCI](https://img.shields.io/circleci/project/github/wulkanowy/wulkanowy.svg?style=flat-square)](https://circleci.com/gh/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.io/api/projects/custom%2B5644%2Fgit%40github.com%3Awulkanowy%2Fwulkanowy.git.svg?type=shield)](https://app.fossa.io/projects/custom%2B5644%2Fgit%40github.com%3Awulkanowy%2Fwulkanowy.git?ref=badge_shield) -[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) +[![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-release-bitrise-signed.apk) -[Pobierz wersję DEV](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/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.io/api/projects/custom%2B5644%2Fgit%40github.com%3Awulkanowy%2Fwulkanowy.git.svg?type=large)](https://app.fossa.io/projects/custom%2B5644%2Fgit%40github.com%3Awulkanowy%2Fwulkanowy.git?ref=badge_large) +Wulkanowy to projekt aplikacji na androida ułatwiającej używanie dziennika VULCANa. diff --git a/app/sonarqube.gradle b/android-sonarqube.gradle similarity index 73% rename from app/sonarqube.gradle rename to android-sonarqube.gradle index 52fd2dd8..b9284eea 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 00000000..ebaca4c8 --- /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 00000000..f7a663ec --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/Client.java @@ -0,0 +1,104 @@ +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.Map; + +public class Client { + + private String protocol; + + private String host; + + private String symbol; + + private Cookies cookies = new Cookies(); + + Client(String protocol, String host, String symbol) { + this.protocol = protocol; + this.host = host; + this.symbol = symbol; + } + + String getHost() { + return host; + } + + public void setSymbol(String symbol) { + this.symbol = symbol; + } + + Map getCookies() { + return cookies.getItems(); + } + + private String getFilledUrl(String url) { + return url + .replace("{schema}", protocol) + .replace("{host}", host.replace(":", "%253A")) + .replace("{symbol}", symbol == null ? "Default" : symbol); + } + + Document getPageByUrl(String url) throws IOException { + Connection.Response response = Jsoup.connect(getFilledUrl(url)) + .followRedirects(true) + .cookies(getCookies()) + .execute(); + + this.cookies.addItems(response.cookies()); + + return response.parse(); + } + + public Document postPageByUrl(String url, String[][] params) throws IOException { + 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 response.parse(); + } + + public String getJsonStringByUrl(String url) throws IOException { + 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 { + 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(); + } +} 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 00000000..c97d9658 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/Cookies.java @@ -0,0 +1,23 @@ +package io.github.wulkanowy.api; + +import java.util.HashMap; +import java.util.Map; + +public class Cookies { + + private Map jar = new HashMap<>(); + + public Map getItems() { + return jar; + } + + public Cookies setItems(Map items) { + this.jar = items; + return this; + } + + public Cookies addItems(Map items) { + this.jar.putAll(items); + return this; + } +} 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 00000000..64a07c97 --- /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 00000000..614b77b6 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/SnP.java @@ -0,0 +1,26 @@ +package io.github.wulkanowy.api; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; + +import java.io.IOException; +import java.util.List; + +import io.github.wulkanowy.api.login.NotLoggedInErrorException; + +public interface SnP { + + String getId(); + + void storeContextCookies() throws IOException, NotLoggedInErrorException; + + String getRowDataChildValue(Element e, int index); + + Document getSnPPageDocument(String url) throws IOException; + + List getSemesters() throws IOException; + + 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 00000000..a8d61913 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java @@ -0,0 +1,119 @@ +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; + +import io.github.wulkanowy.api.login.NotLoggedInErrorException; + +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) { + this.client = client; + } + + StudentAndParent(Client client, String id) { + this(client); + this.id = id; + } + + private String getBaseUrl() { + return BASE_URL.replace("{ID}", getId()); + } + + public String getId() { + return id; + } + + public void storeContextCookies() throws IOException, NotLoggedInErrorException { + client.getPageByUrl(getSnpHomePageUrl()); + } + + String getSnpHomePageUrl() throws IOException, NotLoggedInErrorException { + 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 { + return client.getPageByUrl(getBaseUrl() + url); + } + + public List getSemesters() throws IOException { + 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 00000000..a44e868f --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/Vulcan.java @@ -0,0 +1,196 @@ +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.login.AccountPermissionException; +import io.github.wulkanowy.api.login.BadCredentialsException; +import io.github.wulkanowy.api.login.Login; +import io.github.wulkanowy.api.login.LoginErrorException; +import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.api.login.VulcanOfflineException; +import io.github.wulkanowy.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 String symbol; + + private SnP snp; + + private String protocolSchema = "https"; + + private String logHost = "vulcan.net.pl"; + + private String email; + + private Client client; + + private Login login; + + public void setClient(Client client) { + this.client = client; + } + + public void setLogin(Login login) { + this.login = login; + } + + public void login(String email, String password, String symbol) + throws BadCredentialsException, AccountPermissionException, + LoginErrorException, IOException, VulcanOfflineException { + + setFullEndpointInfo(email); + login = getLogin(); + + this.symbol = login.login(this.email, password, symbol); + } + + public Vulcan login(String email, String password, String symbol, String id) + throws BadCredentialsException, AccountPermissionException, + LoginErrorException, IOException, VulcanOfflineException { + login(email, password, symbol); + + this.id = id; + + return this; + } + + String getProtocolSchema() { + return protocolSchema; + } + + String getLogHost() { + return logHost; + } + + public String getEmail() { + return email; + } + + public String getSymbol() { + return symbol; + } + + private void setFullEndpointInfo(String email) { + String[] creds = email.split("\\\\"); + + this.email = email; + + if (creds.length >= 2) { + String[] url = creds[0].split("://"); + + this.protocolSchema = url[0]; + this.logHost = url[1]; + this.email = creds[2]; + } + } + + protected Client getClient() { + if (null != client) { + return client; + } + + client = new Client(getProtocolSchema(), getLogHost(), symbol); + + return client; + } + + protected Login getLogin() { + if (null != login) { + return login; + } + + login = new Login(getClient()); + + return login; + } + + public SnP getStudentAndParent() throws IOException, NotLoggedInErrorException { + if (0 == getClient().getCookies().size()) { + throw new NotLoggedInErrorException(); + } + + if (null != snp) { + return snp; + } + + snp = createSnp(getClient(), id); + + snp.storeContextCookies(); + + return snp; + } + + SnP createSnp(Client client, String id) { + if (null == id) { + return new StudentAndParent(client); + } + + return new StudentAndParent(client, id); + } + + public AttendanceStatistics getAttendanceStatistics() throws IOException, NotLoggedInErrorException { + return new AttendanceStatistics(getStudentAndParent()); + } + + public AttendanceTable getAttendanceTable() throws IOException, NotLoggedInErrorException { + return new AttendanceTable(getStudentAndParent()); + } + + public ExamsWeek getExamsList() throws IOException, NotLoggedInErrorException { + return new ExamsWeek(getStudentAndParent()); + } + + public GradesList getGradesList() throws IOException, NotLoggedInErrorException { + return new GradesList(getStudentAndParent()); + } + + public SubjectsList getSubjectsList() throws IOException, NotLoggedInErrorException { + return new SubjectsList(getStudentAndParent()); + } + + public AchievementsList getAchievementsList() throws IOException, NotLoggedInErrorException { + return new AchievementsList(getStudentAndParent()); + } + + public NotesList getNotesList() throws IOException, NotLoggedInErrorException { + return new NotesList(getStudentAndParent()); + } + + public SchoolInfo getSchoolInfo() throws IOException, NotLoggedInErrorException { + return new SchoolInfo(getStudentAndParent()); + } + + public TeachersInfo getTeachersInfo() throws IOException, NotLoggedInErrorException { + return new TeachersInfo(getStudentAndParent()); + } + + public Timetable getTimetable() throws IOException, NotLoggedInErrorException { + return new Timetable(getStudentAndParent()); + } + + public BasicInformation getBasicInformation() throws IOException, NotLoggedInErrorException { + return new BasicInformation(getStudentAndParent()); + } + + public FamilyInformation getFamilyInformation() throws IOException, NotLoggedInErrorException { + return new FamilyInformation(getStudentAndParent()); + } + + public Messages getMessages() { + return new Messages(getClient()); + } +} 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 00000000..023a436e --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceStatistics.java @@ -0,0 +1,88 @@ +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.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 { + return getTypesTable(""); + } + + public Types getTypesTable(String tick) throws IOException { + return getTypesTable(tick, -1); + } + + public List getSubjectList() throws IOException { + 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 { + 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 00000000..7c9fc27f --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java @@ -0,0 +1,101 @@ +package io.github.wulkanowy.api.attendance; + +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.generic.Day; +import io.github.wulkanowy.api.generic.Lesson; +import io.github.wulkanowy.api.generic.Week; + +public class AttendanceTable { + + private SnP snp; + + private String attendancePageUrl = "Frekwencja.mvc?data="; + + public AttendanceTable(SnP snp) { + this.snp = snp; + } + + public Week getWeekTable() throws IOException { + return getWeekTable(""); + } + + public Week getWeekTable(String tick) throws IOException { + Element table = snp.getSnPPageDocument(attendancePageUrl + tick) + .select(".mainContainer .presentData").first(); + + Elements headerCells = table.select("thead th"); + List days = new ArrayList<>(); + + for (int i = 1; i < headerCells.size(); i++) { + days.add(new Day().setDate(headerCells.get(i).html().split("
")[1])); + } + + 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++) { + days.get(i - 1).setLesson(getNewLesson(hours.get(i))); + } + } + + String[] dayDescription = headerCells.get(1).html().split("
"); + + return new Week() + .setStartDayDate(dayDescription[1]) + .setDays(days); + } + + private Lesson getNewLesson(Element cell) { + Lesson lesson = new Lesson(); + lesson.setSubject(cell.select("span").text()); + + if (LessonTypes.CLASS_NOT_EXIST.equals(cell.attr("class"))) { + lesson.setNotExist(true); + lesson.setEmpty(true); + + return lesson; + } + + 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; + } + + return lesson; + } +} 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 00000000..a9a1fac4 --- /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 00000000..6deb003e --- /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 00000000..d1b1777d --- /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 00000000..466bfe4f --- /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 00000000..8127e631 --- /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 00000000..b4921b8b --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java @@ -0,0 +1,64 @@ +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.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 { + return getWeek("", true); + } + + public Week getWeek(String tick, final boolean onlyNotEmpty) throws IOException { + 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 00000000..6bf26c04 --- /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 00000000..8dd65327 --- /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 00000000..e38ef3a3 --- /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 00000000..fcf27bef --- /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 00000000..1e46f394 --- /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 00000000..564b3e47 --- /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 00000000..44c45c9a --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java @@ -0,0 +1,82 @@ +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; + +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 { + return getAll(""); + } + + public List getAll(String semester) throws IOException, ParseException { + 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 00000000..c7917810 --- /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 00000000..decf4468 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/grades/SubjectsList.java @@ -0,0 +1,40 @@ +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; + +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 { + 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 00000000..15be359c --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/AccountPermissionException.java @@ -0,0 +1,4 @@ +package io.github.wulkanowy.api.login; + +public class AccountPermissionException extends Exception { +} 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 00000000..4502bb15 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/BadCredentialsException.java @@ -0,0 +1,4 @@ +package io.github.wulkanowy.api.login; + +public class BadCredentialsException extends Exception { +} 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 00000000..e680804a --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/Login.java @@ -0,0 +1,97 @@ +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; + +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 BadCredentialsException, LoginErrorException, + AccountPermissionException, IOException, VulcanOfflineException { + String certificate = sendCredentials(email, password, symbol); + + return sendCertificate(certificate, symbol); + } + + String sendCredentials(String email, String password, String symbol) + throws IOException, BadCredentialsException { + 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, LoginErrorException, AccountPermissionException, VulcanOfflineException { + this.symbol = findSymbol(defaultSymbol, certificate); + client.setSymbol(this.symbol); + + Document html = client.postPageByUrl(LOGIN_ENDPOINT_PAGE_URL, new String[][]{ + {"wa", "wsignin1.0"}, + {"wresult", certificate} + }); + + if (html.getElementsByTag("title").text().equals("Logowanie")) { + throw new AccountPermissionException(); + } + + if (html.getElementsByTag("title").text().equals("Przerwa techniczna")) { + throw new VulcanOfflineException(); + } + + if (!html.select("title").text().equals("Uonet+")) { + 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(); + } +} \ No newline at end of file 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 00000000..eec2b4e4 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/LoginErrorException.java @@ -0,0 +1,4 @@ +package io.github.wulkanowy.api.login; + +public class LoginErrorException extends NotLoggedInErrorException { +} diff --git a/api/src/main/java/io/github/wulkanowy/api/login/NotLoggedInErrorException.java b/api/src/main/java/io/github/wulkanowy/api/login/NotLoggedInErrorException.java new file mode 100644 index 00000000..d33b892d --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/NotLoggedInErrorException.java @@ -0,0 +1,4 @@ +package io.github.wulkanowy.api.login; + +public class NotLoggedInErrorException extends Exception { +} diff --git a/api/src/main/java/io/github/wulkanowy/api/login/VulcanOfflineException.java b/api/src/main/java/io/github/wulkanowy/api/login/VulcanOfflineException.java new file mode 100644 index 00000000..45cf6458 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/VulcanOfflineException.java @@ -0,0 +1,4 @@ +package io.github.wulkanowy.api.login; + +public class VulcanOfflineException extends Exception { +} 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 00000000..90af17ce --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/messages/BadRequestException.java @@ -0,0 +1,4 @@ +package io.github.wulkanowy.api.messages; + +class BadRequestException extends Exception { +} 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 00000000..331ec819 --- /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 00000000..4eb969cd --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/messages/Messages.java @@ -0,0 +1,98 @@ +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.login.NotLoggedInErrorException; + +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, NotLoggedInErrorException, BadRequestException { + return getMessages(RECEIVED_URL); + } + + public List getSent() throws IOException, NotLoggedInErrorException, BadRequestException { + return getMessages(SENT_URL); + } + + public List getDeleted() throws IOException, NotLoggedInErrorException, BadRequestException { + return getMessages(DELETED_URL); + } + + private List getMessages(String url) throws IOException, NotLoggedInErrorException, BadRequestException { + 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, BadRequestException, NotLoggedInErrorException { + 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 00000000..c821e8be --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java @@ -0,0 +1,35 @@ +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; + +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 { + 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 00000000..b1fa68b9 --- /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 00000000..34b8540d --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java @@ -0,0 +1,42 @@ +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; + +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 { + 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 00000000..f135163a --- /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 00000000..4eeb641c --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java @@ -0,0 +1,30 @@ +package io.github.wulkanowy.api.school; + +import org.jsoup.nodes.Element; + +import java.io.IOException; + +import io.github.wulkanowy.api.SnP; + +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 { + 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 00000000..0a3c0957 --- /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 00000000..7d41162e --- /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 00000000..e8af6744 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java @@ -0,0 +1,42 @@ +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; + +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 { + 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 00000000..be1f44e9 --- /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 00000000..1d526494 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java @@ -0,0 +1,215 @@ +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.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 { + return getWeekTable(""); + } + + public Week getWeekTable(final String tick) throws IOException, ParseException { + 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 00000000..3d6fc6d5 --- /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 00000000..875cc8da --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/BasicInformation.java @@ -0,0 +1,69 @@ +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; + +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 { + if (null == studentDataPageDocument) { + studentDataPageDocument = snp.getSnPPageDocument(STUDENT_DATA_PAGE_URL); + } + + return studentDataPageDocument; + } + + public PersonalData getPersonalData() throws IOException { + 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 { + 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 { + 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 00000000..c8556944 --- /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 00000000..555de866 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/FamilyInformation.java @@ -0,0 +1,40 @@ +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; + +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 { + 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 00000000..dfb96674 --- /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 00000000..d2991e61 --- /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/FixtureHelper.java b/api/src/test/java/io/github/wulkanowy/api/FixtureHelper.java new file mode 100644 index 00000000..94dd47b8 --- /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 00000000..89a5b190 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java @@ -0,0 +1,125 @@ +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; + +import io.github.wulkanowy.api.login.NotLoggedInErrorException; + +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); + + 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); + + 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 00000000..b18076cf --- /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 00000000..a80c798a --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java @@ -0,0 +1,86 @@ +package io.github.wulkanowy.api; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.HashMap; +import java.util.Map; + +import io.github.wulkanowy.api.login.Login; +import io.github.wulkanowy.api.login.NotLoggedInErrorException; + +public class VulcanTest { + + private Vulcan vulcan; + + @Before + public void setUp() throws Exception { + vulcan = new Vulcan(); + vulcan.setClient(Mockito.mock(Client.class)); + vulcan.setLogin(Mockito.mock(Login.class)); + } + + @Test + public void setFullEndpointInfoTest() throws Exception { + vulcan.login("http://fakelog.net\\\\admin", "pass", "Default", "123"); + + Assert.assertEquals("http", vulcan.getProtocolSchema()); + Assert.assertEquals("fakelog.net", vulcan.getLogHost()); + Assert.assertEquals("admin", vulcan.getEmail()); + } + + @Test + public void getClientTwiceTest() throws Exception { + Vulcan vulcan = new Vulcan(); + Assert.assertTrue(vulcan.getClient().equals(vulcan.getClient())); + } + + @Test + public void getLoginTwiceTest() throws Exception { + Vulcan vulcan = new Vulcan(); + Assert.assertTrue(vulcan.getLogin().equals(vulcan.getLogin())); + } + + @Test(expected = NotLoggedInErrorException.class) + public void getStudentAndParentNotLoggedInTest() throws Exception { + vulcan.getStudentAndParent(); + } + + @Test + public void getStudentAndParentTwiceTest() throws Exception { + Client client = Mockito.mock(Client.class); + Map cookies = new HashMap<>(); + cookies.put("test", "test"); + Mockito.when(client.getCookies()).thenReturn(cookies); + + SnP snp = Mockito.mock(StudentAndParent.class); + Mockito.doNothing().when(snp).storeContextCookies(); + + Vulcan vulcan = Mockito.mock(Vulcan.class); + Mockito.when(vulcan.getClient()).thenReturn(client); + Mockito.when(vulcan.getStudentAndParent()).thenCallRealMethod(); + Mockito.when(vulcan.createSnp(Mockito.any(Client.class), Mockito.any())).thenReturn(snp); + + vulcan.getStudentAndParent(); + vulcan.getStudentAndParent(); + } + + @Test + public void createSnPTest() throws Exception { + vulcan.login("wulkanowy@wulkanowy.io", "wulkanowy123", "wulkan"); + + SnP snp1 = vulcan.createSnp(Mockito.mock(Client.class), null); + Assert.assertEquals(null, snp1.getId()); + + SnP snp2 = vulcan.createSnp(Mockito.mock(Client.class), "wulkan"); + Assert.assertEquals("wulkan", snp2.getId()); + + } + + @Test(expected = NotLoggedInErrorException.class) + public void getAttendanceExceptionText() throws Exception { + vulcan.getAttendanceTable(); + } +} 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 00000000..ca50f378 --- /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 00000000..1420a475 --- /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("31.08.2015", excellent.getWeekTable().getStartDayDate()); + Assert.assertEquals("05.09.2016", 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("31.08.2015", excellent.getWeekTable().getDay(0).getDate()); + Assert.assertEquals("02.09.2015", excellent.getWeekTable().getDay(2).getDate()); + Assert.assertEquals("04.09.2015", excellent.getWeekTable().getDay(4).getDate()); + + Assert.assertEquals("05.09.2016", full.getWeekTable().getDay(0).getDate()); + Assert.assertEquals("07.09.2016", full.getWeekTable().getDay(2).getDate()); + Assert.assertEquals("09.09.2016", 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 00000000..9b57ddd6 --- /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 00000000..bee5b4fe --- /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 00000000..d3228378 --- /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 00000000..335ab84b --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java @@ -0,0 +1,104 @@ +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(expected = VulcanOfflineException.class) + public void sendCertificateVulcanOfflineTest() throws Exception { + Login login = new Login(getClient("PrzerwaTechniczna.html")); + + 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 00000000..44880a88 --- /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.login.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 00000000..0d7774bb --- /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 00000000..d76c0648 --- /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 00000000..947db9b8 --- /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 00000000..b0da356b --- /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 00000000..65267634 --- /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 00000000..6b729c95 --- /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 00000000..b4f858ec --- /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 00000000..f4b712c1 --- /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 00000000..40e69161 --- /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 00000000..5eaf50df --- /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 00000000..aa995305 --- /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 00000000..ecc48cf1 --- /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 00000000..a229d1d5 --- /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 00000000..023b5405 --- /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 00000000..0f6da414 --- /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 00000000..2911f1d2 --- /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 00000000..a8496cd1 --- /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 00000000..08713188 --- /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-success.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-success.html new file mode 100644 index 00000000..23c27a3f --- /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 00000000..062f9b60 --- /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 00000000..549b2d42 --- /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 00000000..1ba54a32 --- /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 00000000..326390ed --- /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 00000000..36f89aa1 --- /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 00000000..ae976af1 --- /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 00000000..c8d55a96 --- /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 00000000..b15bb6e7 --- /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 00000000..50e74058 --- /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 00000000..05a698a0 --- /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 00000000..53eb0485 --- /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 00000000..09555cf3 --- /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 00000000..8bcc9794 --- /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 00000000..c54dd861 --- /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 6382469d..1887df3a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,136 +1,106 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-android-extensions' -apply plugin: 'io.fabric' -apply plugin: 'com.github.triplet.play' -apply from: 'jacoco.gradle' -apply from: 'sonarqube.gradle' - -android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' - - playAccountConfigs { - defaultAccountConfig { - serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") - pk12File = file('key.p12') - } +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: 'org.greenrobot.greendao' +apply plugin: 'io.fabric' +apply from: '../jacoco.gradle' +apply from: '../android-sonarqube.gradle' + +android { + compileSdkVersion 27 + buildToolsVersion "27.0.3" defaultConfig { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 - targetSdkVersion 28 - versionCode 19 - versionName "0.6.0" - multiDexEnabled true - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + targetSdkVersion 27 + versionCode 2 + versionName "0.1.1" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true - playAccountConfig = playAccountConfigs.defaultAccountConfig - manifestPlaceholders = [crashlytics_enabled: project.hasProperty("enableCrashlytics")] - } - - 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") - } + manifestPlaceholders = [ + fabricApiKey: System.getenv("FABRIC_API_KEY") ?: "null" + ] } buildTypes { release { - buildConfigField "boolean", "CRASHLYTICS_ENABLED", "true" - minifyEnabled true - useProguard false - 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' + testOptions { + unitTests.all { + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen { false } + showStandardStreams = true + } + } } } -androidExtensions { - experimental = true -} - -play { - track = 'alpha' - uploadImages = true -} - -configurations.all { - resolutionStrategy.force "com.squareup.okhttp3:okhttp-urlconnection:3.11.0" +greendao { + schemaVersion 20 + generateTests = true } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - implementation('io.github.wulkanowy:api:0.6.0') { 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.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.multidex:multidex:2.0.0' + 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 'com.takisoft.preferencex:preferencex:1.0.0' - implementation "com.mikepenz:aboutlibraries:6.2.0" - implementation "com.firebase:firebase-jobdispatcher:0.8.5" + 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 "com.google.dagger:dagger-android-support:2.19" - kapt "com.google.dagger:dagger-compiler:2.19" - kapt "com.google.dagger:dagger-android-processor:2.19" + debugImplementation 'com.amitshekhar.android:debug-db:1.0.1' + debugImplementation 'net.zetetic:android-database-sqlcipher:3.5.9' - implementation "androidx.room:room-runtime:2.1.0-alpha03" - implementation "androidx.room:room-rxjava2:2.1.0-alpha03" - kapt "androidx.room:room-compiler:2.1.0-alpha03" + testImplementation 'junit:junit:4.12' + testImplementation 'org.mockito:mockito-core:2.13.0' - implementation "eu.davidea:flexible-adapter:5.1.0" - implementation "eu.davidea:flexible-adapter-ui:1.0.0" - - implementation "com.aurelhubert:ahbottomnavigation:2.2.0" - implementation 'com.ncapdevi:frag-nav:3.0.0' - - implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.1' - implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' - implementation "io.reactivex.rxjava2:rxjava:2.2.4" - - implementation "com.jakewharton.threetenabp:threetenabp:1.1.0" - - implementation "com.jakewharton.timber:timber:4.7.1" - implementation "at.favre.lib:slf4j-timber:1.0.1" - - implementation 'com.google.firebase:firebase-core:16.0.6' - implementation 'com.crashlytics.sdk.android:crashlytics:2.9.7' - - debugImplementation "com.amitshekhar.android:debug-db:1.0.4" - - testImplementation "junit:junit:4.12" - testImplementation "io.mockk:mockk:1.8.13.kotlin13" - testImplementation "org.mockito:mockito-inline:2.23.4" - 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 'org.mockito:mockito-android:2.23.4' - 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 4f94ef19..00000000 --- a/app/jacoco.gradle +++ /dev/null @@ -1,47 +0,0 @@ -apply plugin: "jacoco" - -jacoco { - toolVersion "0.8.2" - 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 d9811213..00000000 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 e9b6d06e..00000000 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 9ee1737f..00000000 --- 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 cebd3d94..34ed4c38 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,36 +1,25 @@ -# Optimizations --optimizationpasses 5 --optimizations !code/simplification/arithmetic,!field/*,!class/merging/* --dontusemixedcaseclassnames --dontskipnonpubliclibraryclasses --dontskipnonpubliclibraryclassmembers --dontpreverify --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 --renamesourcefileattribute SourceFile --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 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/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 00000000..e5330b62 --- /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/DayTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DayTest.java new file mode 100644 index 00000000..34c4c4c5 --- /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 00000000..ea026559 --- /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/LessonTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/LessonTest.java new file mode 100644 index 00000000..c8690ff9 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/LessonTest.java @@ -0,0 +1,24 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +public class LessonTest extends AbstractDaoTestLongPk { + + public LessonTest() { + super(LessonDao.class); + } + + @Override + protected Lesson createEntity(Long key) { + Lesson entity = new Lesson(); + 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/SubjectTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SubjectTest.java new file mode 100644 index 00000000..81a2e724 --- /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/WeekTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/WeekTest.java new file mode 100644 index 00000000..86e7a8be --- /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/repositories/local/AttendanceLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/AttendanceLocalTest.kt deleted file mode 100644 index 8cbd13cd..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/AttendanceLocalTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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, "", ""), - Attendance(1, 2, LocalDate.of(2018, 9, 14), 0, "", ""), - Attendance(1, 2, LocalDate.of(2018, 9, 17), 0, "", "") - )) - - val attendance = attendanceLocal - .getAttendance(Semester(1, 1, 2, "", 3, 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/local/ExamLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt deleted file mode 100644 index c60348e3..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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, 1, 2, "", 3, 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/local/StudentLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/StudentLocalTest.kt deleted file mode 100644 index 732dc5bc..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/StudentLocalTest.kt +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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 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, sharedHelper, 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)) - .blockingGet() - assert(studentLocal.isStudentSaved) - - val student = studentLocal.getCurrentStudent(true).blockingGet() - assertEquals("23", student.schoolSymbol) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/TimetableLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/TimetableLocalTest.kt deleted file mode 100644 index df0a7994..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/TimetableLocalTest.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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 io.github.wulkanowy.data.db.entities.Timetable -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 -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( - Timetable(1, 2, 1, LocalDateTime.now(), LocalDateTime.now(), - LocalDate.of(2018, 9, 10), "", "", "", "", ""), - Timetable(1, 2, 1, LocalDateTime.now(), LocalDateTime.now(), - LocalDate.of(2018, 9, 14), "", "", "", "", ""), - Timetable(1, 2, 1, LocalDateTime.now(), LocalDateTime.now(), - LocalDate.of(2018, 9, 17), "", "", "", "", "") - )) - - val exams = timetableDb.getTimetable( - Semester(0, 1, 2, "3", 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/utils/security/ScramblerTest.java b/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.java new file mode 100644 index 00000000..2fd1904b --- /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 0c47e6bb..00000000 --- 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 e9303986..00000000 --- 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 736f7906..00000000 Binary files a/app/src/debug/google-services.json.gpg and /dev/null differ diff --git a/app/src/debug/res/values-pl/strings.xml b/app/src/debug/res/values-pl/strings.xml deleted file mode 100644 index c90641dd..00000000 --- a/app/src/debug/res/values-pl/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Wulkanowy DEV - diff --git a/app/src/debug/res/values/strings.xml b/app/src/debug/res/values/strings.xml deleted file mode 100644 index c90641dd..00000000 --- a/app/src/debug/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Wulkanowy DEV - diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 643fd292..f1733681 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,28 +1,27 @@ - + android:supportsRtl="true" + android:theme="@style/WulkanowyTheme"> + android:name=".ui.splash.SplashActivity" + android:configChanges="orientation|screenSize" + android:noHistory="true" + android:theme="@style/WulkanowyTheme.SplashTheme"> @@ -30,40 +29,26 @@ + android:label="@string/activity_dashboard_text" /> - - - - - - - - + android:name="io.fabric.ApiKey" + android:value="${fabricApiKey}" /> + diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png index de216d36..7cad5c2f 100644 Binary files a/app/src/main/ic_launcher-web.png 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 00000000..4b8866ce --- /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 628d6ce6..00000000 --- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy - -import android.content.Context -import androidx.appcompat.app.AppCompatDelegate -import androidx.multidex.MultiDex -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.PreferencesRepository -import io.github.wulkanowy.di.DaggerAppComponent -import io.github.wulkanowy.utils.CrashlyticsTree -import io.github.wulkanowy.utils.DebugLogTree -import timber.log.Timber -import javax.inject.Inject - -class WulkanowyApp : DaggerApplication() { - - @Inject - lateinit var prefRepository: PreferencesRepository - - 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) - } - - 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()) - } - - 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 5b7b1072..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt +++ /dev/null @@ -1,27 +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 - useNewStudent = false - host = URL(student.endpoint).run { host + ":$port".removeSuffix(":-1") } - ssl = student.endpoint.startsWith("https") - loginType = Api.LoginType.valueOf(student.loginType) - } - } - - fun initApi(email: String, password: String, symbol: String, endpoint: String) { - initApi(Student(email = email, password = password, symbol = symbol, endpoint = endpoint, loginType = "AUTO")) - } -} 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 00000000..04460a07 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/Repository.java @@ -0,0 +1,143 @@ +package io.github.wulkanowy.data; + + +import java.io.IOException; +import java.text.ParseException; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.github.wulkanowy.api.login.AccountPermissionException; +import io.github.wulkanowy.api.login.BadCredentialsException; +import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.api.login.VulcanOfflineException; +import io.github.wulkanowy.data.db.dao.entities.Account; +import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.dao.entities.Grade; +import io.github.wulkanowy.data.db.dao.entities.GradeDao; +import io.github.wulkanowy.data.db.dao.entities.Week; +import io.github.wulkanowy.data.db.dao.entities.WeekDao; +import io.github.wulkanowy.data.db.resources.ResourcesContract; +import io.github.wulkanowy.data.db.shared.SharedPrefContract; +import io.github.wulkanowy.data.sync.SyncContract; +import io.github.wulkanowy.data.sync.login.LoginSyncContract; +import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract; +import io.github.wulkanowy.di.annotations.SyncGrades; +import io.github.wulkanowy.di.annotations.SyncSubjects; +import io.github.wulkanowy.utils.security.CryptoException; + +@Singleton +public class Repository implements RepositoryContract { + + private final SharedPrefContract sharedPref; + + private final ResourcesContract resources; + + private final DaoSession daoSession; + + private final LoginSyncContract loginSync; + + private final TimetableSyncContract timetableSync; + + private final SyncContract gradeSync; + + private final SyncContract subjectSync; + + @Inject + Repository(SharedPrefContract sharedPref, + ResourcesContract resources, + DaoSession daoSession, + LoginSyncContract loginSync, + TimetableSyncContract timetableSync, + @SyncGrades SyncContract gradeSync, + @SyncSubjects SyncContract subjectSync) { + this.sharedPref = sharedPref; + this.resources = resources; + this.daoSession = daoSession; + this.loginSync = loginSync; + this.timetableSync = timetableSync; + this.gradeSync = gradeSync; + this.subjectSync = subjectSync; + } + + @Override + public long getCurrentUserId() { + return sharedPref.getCurrentUserId(); + } + + @Override + public String[] getSymbolsKeysArray() { + return resources.getSymbolsKeysArray(); + } + + @Override + public String[] getSymbolsValuesArray() { + return resources.getSymbolsValuesArray(); + } + + @Override + public String getErrorLoginMessage(Exception e) { + return resources.getErrorLoginMessage(e); + } + + @Override + public void loginUser(String email, String password, String symbol) + throws NotLoggedInErrorException, AccountPermissionException, IOException, + CryptoException, VulcanOfflineException, BadCredentialsException { + loginSync.loginUser(email, password, symbol); + } + + @Override + public void loginCurrentUser() throws NotLoggedInErrorException, AccountPermissionException, + IOException, CryptoException, VulcanOfflineException, BadCredentialsException { + loginSync.loginCurrentUser(); + } + + @Override + public void syncGrades() throws NotLoggedInErrorException, IOException, ParseException { + gradeSync.sync(); + } + + @Override + public void syncSubjects() throws NotLoggedInErrorException, IOException, ParseException { + subjectSync.sync(); + } + + @Override + public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException { + timetableSync.syncTimetable(); + } + + @Override + public void syncTimetable(String date) throws NotLoggedInErrorException, IOException, ParseException { + timetableSync.syncTimetable(date); + } + + @Override + public void syncAll() throws NotLoggedInErrorException, IOException, ParseException { + syncSubjects(); + syncGrades(); + syncTimetable(); + } + + @Override + public Account getCurrentUser() { + return daoSession.getAccountDao().load(sharedPref.getCurrentUserId()); + } + + @Override + public Week getWeek(String date) { + return daoSession.getWeekDao().queryBuilder() + .where(WeekDao.Properties.StartDayDate.eq(date), + WeekDao.Properties.UserId.eq(getCurrentUserId())) + .unique(); + } + + @Override + public List 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 00000000..fcdf29ed --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java @@ -0,0 +1,34 @@ +package io.github.wulkanowy.data; + +import java.io.IOException; +import java.text.ParseException; +import java.util.List; + +import javax.inject.Singleton; + +import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.data.db.dao.entities.Account; +import io.github.wulkanowy.data.db.dao.entities.Grade; +import io.github.wulkanowy.data.db.dao.entities.Week; +import io.github.wulkanowy.data.db.resources.ResourcesContract; +import io.github.wulkanowy.data.sync.login.LoginSyncContract; +import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract; + +@Singleton +public interface RepositoryContract extends ResourcesContract, LoginSyncContract, + TimetableSyncContract { + + long getCurrentUserId(); + + void syncGrades() throws NotLoggedInErrorException, IOException, ParseException; + + void syncSubjects() throws NotLoggedInErrorException, IOException, ParseException; + + void syncAll() throws NotLoggedInErrorException, IOException, ParseException; + + Account getCurrentUser(); + + Week getWeek(String date); + + List 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 3c58ea4b..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt +++ /dev/null @@ -1,101 +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.SocketInternetObservingStrategy -import dagger.Module -import dagger.Provides -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.AppDatabase -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(SocketInternetObservingStrategy()) - .host("www.google.com") - .build() - } - - @Singleton - @Provides - fun provideApi(): 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)) - } - } - - @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 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 -} 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 30c957b4..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ /dev/null @@ -1,88 +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.ExamDao -import io.github.wulkanowy.data.db.dao.GradeDao -import io.github.wulkanowy.data.db.dao.GradeSummaryDao -import io.github.wulkanowy.data.db.dao.MessagesDao -import io.github.wulkanowy.data.db.dao.HomeworkDao -import io.github.wulkanowy.data.db.dao.NoteDao -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.Exam -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Homework -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.db.entities.Subject -import io.github.wulkanowy.data.db.entities.Timetable -import javax.inject.Singleton - -@Singleton -@Database( - entities = [ - Student::class, - Semester::class, - Exam::class, - Timetable::class, - Attendance::class, - AttendanceSummary::class, - Grade::class, - GradeSummary::class, - Message::class, - Note::class, - Homework::class, - Subject::class - ], - version = 1, - exportSchema = false -) -@TypeConverters(Converters::class) -abstract class AppDatabase : RoomDatabase() { - - companion object { - fun newInstance(context: Context): AppDatabase { - return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database") - .setJournalMode(TRUNCATE) - .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 messagesDao: MessagesDao - - abstract val noteDao: NoteDao - - abstract val homeworkDao: HomeworkDao - - abstract val subjectDao: SubjectDao -} 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 a550df89..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.wulkanowy.data.db - -import androidx.room.TypeConverter -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) } -} 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 656b39d4..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt +++ /dev/null @@ -1,33 +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 putBoolean(key: String, value: Boolean) { - sharedPref.edit().putBoolean(key, value).apply() - } - - fun getBoolean(key: String, defaultValue: Boolean): Boolean { - return sharedPref.getBoolean(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 d3c4f146..00000000 --- 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 a7413de5..00000000 --- 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/DbHelper.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java new file mode 100644 index 00000000..199a6c8b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.db.dao; + +import android.content.Context; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.github.wulkanowy.data.db.dao.entities.DaoMaster; +import io.github.wulkanowy.di.annotations.ApplicationContext; +import io.github.wulkanowy.di.annotations.DatabaseInfo; + +@Singleton +public class DbHelper extends DaoMaster.DevOpenHelper { + + @Inject + DbHelper(@ApplicationContext Context context, @DatabaseInfo String dbName) { + super(context, dbName); + } +} 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 06cd5613..00000000 --- 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 629f201d..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.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.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 update(grade: Grade) - - @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> - - @Query("SELECT * FROM Grades WHERE is_read = 0 AND semester_id = :semesterId AND student_id = :studentId") - fun loadAllNew(semesterId: Int, studentId: Int): 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 3530118c..00000000 --- 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 grades_summary 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 4127460f..00000000 --- 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 = :date") - fun loadAll(semesterId: Int, 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 5018b690..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt +++ /dev/null @@ -1,37 +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): List - - @Delete - fun deleteAll(messages: List) - - @Update - fun update(message: Message) - - @Update - fun updateAll(messages: List) - - @Query("SELECT * FROM Messages WHERE student_id = :studentId AND real_id = :id") - fun loadOne(studentId: Int, id: Int): Maybe - - @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder ORDER BY date DESC") - fun load(studentId: Int, folder: Int): Maybe> - - @Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC") - fun loadDeleted(studentId: Int): Maybe> - - @Query("SELECT * FROM Messages WHERE unread = 1 AND student_id = :studentId") - fun loadNewMessages(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 2c182860..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.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.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 update(note: Note) - - @Update - fun updateAll(notes: List) - - @Delete - fun deleteAll(notes: List) - - @Query("SELECT * FROM Notes WHERE semester_id = :semesterId AND student_id = :studentId") - fun loadAll(semesterId: Int, studentId: Int): Maybe> - - @Query("SELECT * FROM Notes WHERE is_read = 0 AND semester_id = :semesterId AND student_id = :studentId") - fun loadNew(semesterId: Int, studentId: 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 44de31d8..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy.IGNORE -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(onConflict = IGNORE) - fun insertAll(semester: List) - - @Query("SELECT * FROM Semesters WHERE student_id = :studentId") - fun loadAll(studentId: Int): Maybe> - - @Query("UPDATE Semesters SET is_current = 1 WHERE semester_id = :semesterId AND diary_id = :diaryId") - fun updateCurrent(semesterId: Int, diaryId: Int) - - @Query("UPDATE Semesters SET is_current = 0 WHERE student_id = :studentId") - fun resetCurrent(studentId: Int) -} 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 76e29539..00000000 --- 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.FAIL -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 = FAIL) - 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 student_id = :studentId") - fun updateCurrent(studentId: Int) - - @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 725a371a..00000000 --- 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 abe21361..00000000 --- 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 00000000..20a47ac2 --- /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/Day.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java new file mode 100644 index 00000000..15a06309 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java @@ -0,0 +1,214 @@ +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 = "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 lessons; + + /** + * 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 isFreeDay() { + return isFreeDay; + } + + public Day setFreeDay(boolean freeDay) { + isFreeDay = freeDay; + return this; + } + + public String getFreeDayName() { + return freeDayName; + } + + public Day setFreeDayName(String freeDayName) { + this.freeDayName = freeDayName; + return this; + } + + public boolean getIsFreeDay() { + return this.isFreeDay; + } + + public void setIsFreeDay(boolean isFreeDay) { + this.isFreeDay = isFreeDay; + } + + /** + * 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 = 1552857303) + public List getLessons() { + if (lessons == null) { + final DaoSession daoSession = this.daoSession; + if (daoSession == null) { + throw new DaoException("Entity is detached from DAO context"); + } + LessonDao targetDao = daoSession.getLessonDao(); + List lessonsNew = targetDao._queryDay_Lessons(id); + synchronized (this) { + if (lessons == null) { + lessons = lessonsNew; + } + } + } + return lessons; + } + + /** + * Resets a to-many relationship, making the next get call to query for a fresh result. + */ + @Generated(hash = 1769801440) + public synchronized void resetLessons() { + lessons = 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 00000000..632f9bcc --- /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/Lesson.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Lesson.java new file mode 100644 index 00000000..db9be944 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Lesson.java @@ -0,0 +1,353 @@ +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 = "Lessons", + active = true, + indexes = {@Index(value = "dayId,date,startTime,endTime", unique = true)} +) +public class Lesson 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 = 610143130) + private transient LessonDao myDao; + + @Generated(hash = 140778287) + public Lesson(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 = 1669664117) + public Lesson() { + } + + 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 number; + } + + public Lesson setNumber(String number) { + this.number = number; + return this; + } + + 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 Lesson setGroupName(String groupName) { + this.groupName = groupName; + return this; + } + + public String getStartTime() { + return startTime; + } + + public Lesson setStartTime(String startTime) { + this.startTime = startTime; + return this; + } + + public String getEndTime() { + return endTime; + } + + public Lesson setEndTime(String endTime) { + this.endTime = endTime; + return this; + } + + 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 Lesson setDivisionIntoGroups(boolean divisionIntoGroups) { + isDivisionIntoGroups = divisionIntoGroups; + return this; + } + + public boolean isPlanning() { + return isPlanning; + } + + public Lesson setPlanning(boolean planning) { + isPlanning = planning; + return this; + } + + public boolean isRealized() { + return isRealized; + } + + public Lesson setRealized(boolean realized) { + isRealized = realized; + return this; + } + + public boolean isMovedOrCanceled() { + return isMovedOrCanceled; + } + + public Lesson setMovedOrCanceled(boolean movedOrCanceled) { + isMovedOrCanceled = movedOrCanceled; + return this; + } + + public boolean isNewMovedInOrChanged() { + return isNewMovedInOrChanged; + } + + public Lesson setNewMovedInOrChanged(boolean newMovedInOrChanged) { + isNewMovedInOrChanged = newMovedInOrChanged; + return this; + } + + public boolean getIsEmpty() { + return this.isEmpty; + } + + public void setIsEmpty(boolean isEmpty) { + this.isEmpty = isEmpty; + } + + public boolean getIsDivisionIntoGroups() { + return this.isDivisionIntoGroups; + } + + public void setIsDivisionIntoGroups(boolean isDivisionIntoGroups) { + this.isDivisionIntoGroups = isDivisionIntoGroups; + } + + public boolean getIsPlanning() { + return this.isPlanning; + } + + public void setIsPlanning(boolean isPlanning) { + this.isPlanning = isPlanning; + } + + public boolean getIsRealized() { + return this.isRealized; + } + + public void setIsRealized(boolean isRealized) { + this.isRealized = isRealized; + } + + public boolean getIsMovedOrCanceled() { + return this.isMovedOrCanceled; + } + + public void setIsMovedOrCanceled(boolean isMovedOrCanceled) { + this.isMovedOrCanceled = isMovedOrCanceled; + } + + public boolean getIsNewMovedInOrChanged() { + return this.isNewMovedInOrChanged; + } + + public void setIsNewMovedInOrChanged(boolean isNewMovedInOrChanged) { + this.isNewMovedInOrChanged = isNewMovedInOrChanged; + } + + /** + * 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 = 2078826279) + public void __setDaoSession(DaoSession daoSession) { + this.daoSession = daoSession; + myDao = daoSession != null ? daoSession.getLessonDao() : 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 00000000..6bcce622 --- /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/Week.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java new file mode 100644 index 00000000..c82f3277 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java @@ -0,0 +1,148 @@ +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 = ""; + + @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 = 36829814) + public Week(Long id, Long userId, String startDayDate) { + this.id = id; + this.userId = userId; + this.startDayDate = startDayDate; + } + + @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; + } + + /** + * 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 7588201b..00000000 --- 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") - var studentId: Int, - - @ColumnInfo(name = "diary_id") - var diaryId: Int, - - var date: LocalDate, - - var number: Int, - - var subject: String, - - var name: String, - - var presence: Boolean = false, - - var absence: Boolean = false, - - var exemption: Boolean = false, - - var lateness: Boolean = false, - - var excused: Boolean = false, - - var deleted: Boolean = false -) : 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 de2de98f..00000000 --- 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") - var studentId: Int, - - @ColumnInfo(name = "diary_id") - var diaryId: Int, - - @ColumnInfo(name = "subject_id") - var subjectId: Int = 0, - - 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/Exam.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt deleted file mode 100644 index daa886d5..00000000 --- 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") - var studentId: Int, - - @ColumnInfo(name = "diary_id") - var diaryId: Int, - - var date: LocalDate, - - @ColumnInfo(name = "entry_date") - var entryDate: LocalDate = LocalDate.now(), - - var subject: String, - - var group: String, - - var type: String, - - var description: String, - - var teacher: String, - - @ColumnInfo(name = "teacher_symbol") - var 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 d665f9d2..00000000 --- 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") - var semesterId: Int, - - @ColumnInfo(name = "student_id") - var studentId: Int, - - var subject: String, - - var entry: String, - - var value: Int, - - var modifier: Double, - - var comment: String, - - var color: String, - - @ColumnInfo(name = "grade_symbol") - var gradeSymbol: String, - - var description: String, - - var weight: String, - - var weightValue: Int, - - var date: LocalDate, - - var 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/GradeSummary.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt deleted file mode 100644 index 74d709bb..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.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 - -@Entity(tableName = "Grades_Summary") -data class GradeSummary( - - @ColumnInfo(name = "semester_id") - var semesterId: Int, - - @ColumnInfo(name = "student_id") - var studentId: Int, - - var subject: String, - - var predictedGrade: String, - - var 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 705d1d63..00000000 --- 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") - var semesterId: Int, - - @ColumnInfo(name = "student_id") - var studentId: Int, - - var date: LocalDate, - - @ColumnInfo(name = "entry_date") - var entryDate: LocalDate, - - var subject: String, - - var content: String, - - var teacher: String, - - @ColumnInfo(name = "teacher_symbol") - var teacherSymbol: String - -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} 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 320e9322..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt +++ /dev/null @@ -1,56 +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") - var studentId: Int? = null, - - @ColumnInfo(name = "real_id") - val realId: Int? = null, - - @ColumnInfo(name = "message_id") - val messageId: Int? = null, - - @ColumnInfo(name = "sender_name") - val sender: String? = null, - - @ColumnInfo(name = "sender_id") - val senderId: Int? = null, - - @ColumnInfo(name = "recipient_id") - val recipientId: Int? = null, - - @ColumnInfo(name = "recipient_name") - val recipient: String? = "", - - val subject: String = "", - - val date: LocalDateTime? = null, - - @ColumnInfo(name = "folder_id") - val folderId: Int = 0, - - var unread: Boolean? = false, - - val unreadBy: Int? = 0, - - val readBy: Int? = 0, - - val removed: Boolean = false -) : 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 1f61f087..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt +++ /dev/null @@ -1,35 +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 = "semester_id") - var semesterId: Int, - - @ColumnInfo(name = "student_id") - var studentId: Int, - - var date: LocalDate, - - var teacher: String, - - var category: String, - - var content: String -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 - - @ColumnInfo(name = "is_read") - var isRead: Boolean = false - - @ColumnInfo(name = "is_notified") - var isNotified: Boolean = true -} 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 509a692e..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt +++ /dev/null @@ -1,31 +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( - - @PrimaryKey(autoGenerate = true) - var id: Long = 0, - - @ColumnInfo(name = "student_id") - var studentId: Int, - - @ColumnInfo(name = "diary_id") - var diaryId: Int, - - @ColumnInfo(name = "diary_name") - var diaryName: String, - - @ColumnInfo(name = "semester_id") - var semesterId: Int, - - @ColumnInfo(name = "semester_name") - var semesterName: Int, - - @ColumnInfo(name = "is_current") - var isCurrent: Boolean = false -) 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 1fe4cf8d..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.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 = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id"], unique = true)]) -data class Student( - - @PrimaryKey(autoGenerate = true) - var id: Long = 0, - - var endpoint: String, - - var loginType: String, - - var email: String, - - var password: String, - - var symbol: String = "", - - @ColumnInfo(name = "student_id") - var studentId: Int = 0, - - @ColumnInfo(name = "student_name") - var studentName: String = "", - - @ColumnInfo(name = "school_id") - var schoolSymbol: String = "", - - @ColumnInfo(name = "school_name") - var schoolName: String = "", - - @ColumnInfo(name = "is_current") - var isCurrent: Boolean = false -) 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 45306be3..00000000 --- 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") - var studentId: Int, - - @ColumnInfo(name = "diary_id") - var diaryId: Int, - - @ColumnInfo(name = "real_id") - var realId: Int, - - var 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 2aa1eafa..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt +++ /dev/null @@ -1,44 +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") - var studentId: Int, - - @ColumnInfo(name = "diary_id") - var diaryId: Int, - - val number: Int = 0, - - val start: LocalDateTime = LocalDateTime.now(), - - val end: LocalDateTime = LocalDateTime.now(), - - val date: LocalDate, - - val subject: String, - - val group: String, - - val room: String, - - val teacher: String, - - val info: String, - - val changes: Boolean = false, - - val canceled: Boolean = false -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} 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 00000000..8e7227d3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/resources/AppResources.java @@ -0,0 +1,59 @@ +package io.github.wulkanowy.data.db.resources; + +import android.content.Context; +import android.content.res.Resources; + +import java.io.IOException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.github.wulkanowy.R; +import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.api.login.VulcanOfflineException; +import io.github.wulkanowy.di.annotations.ApplicationContext; +import io.github.wulkanowy.utils.AppConstant; +import io.github.wulkanowy.utils.LogUtils; +import io.github.wulkanowy.utils.security.CryptoException; + +@Singleton +public class AppResources implements ResourcesContract { + + private Resources resources; + + @Inject + AppResources(@ApplicationContext Context context) { + resources = context.getResources(); + } + + @Override + public String[] getSymbolsKeysArray() { + return resources.getStringArray(R.array.symbols); + } + + @Override + public String[] getSymbolsValuesArray() { + return resources.getStringArray(R.array.symbols_values); + } + + @Override + public String getErrorLoginMessage(Exception exception) { + LogUtils.error(AppConstant.APP_NAME + " encountered a error", exception); + + if (exception instanceof CryptoException) { + return resources.getString(R.string.encrypt_failed_text); + } else if (exception instanceof UnknownHostException) { + return resources.getString(R.string.noInternet_text); + } else if (exception instanceof SocketTimeoutException) { + return resources.getString(R.string.generic_timeout_error); + } else if (exception instanceof NotLoggedInErrorException || exception instanceof IOException) { + return resources.getString(R.string.login_denied_text); + } else if (exception instanceof VulcanOfflineException) { + return resources.getString(R.string.error_host_offline); + } else { + return exception.getMessage(); + } + } +} 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 00000000..3768ad2c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java @@ -0,0 +1,10 @@ +package io.github.wulkanowy.data.db.resources; + +public interface ResourcesContract { + + String[] getSymbolsKeysArray(); + + String[] getSymbolsValuesArray(); + + String getErrorLoginMessage(Exception e); +} 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 00000000..ba437f88 --- /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 00000000..7f540acf --- /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/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt deleted file mode 100644 index eb7230ae..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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.data.repositories.local.AttendanceLocal -import io.github.wulkanowy.data.repositories.remote.AttendanceRemote -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/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt deleted file mode 100644 index 8e21b12c..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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.github.wulkanowy.data.repositories.local.AttendanceSummaryLocal -import io.github.wulkanowy.data.repositories.remote.AttendanceSummaryRemote -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/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt deleted file mode 100644 index cb78df53..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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.data.repositories.local.ExamLocal -import io.github.wulkanowy.data.repositories.remote.ExamRemote -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/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt deleted file mode 100644 index 42266955..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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.repositories.local.GradeLocal -import io.github.wulkanowy.data.repositories.remote.GradeRemote -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(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 -> - local.deleteGrades(oldGrades - newGrades) - local.saveGrades((newGrades - oldGrades) - .onEach { - if (oldGrades.isNotEmpty()) it.isRead = false - if (notify) it.isNotified = false - }) - } - }.flatMap { local.getGrades(semester).toSingle(emptyList()) }) - } - - fun getNewGrades(semester: Semester): Single> { - return local.getNewGrades(semester).toSingle(emptyList()) - } - - fun updateGrade(grade: Grade): Completable { - return local.updateGrade(grade) - } - - fun updateGrades(grades: List): Completable { - return local.updateGrades(grades) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeSummaryRepository.kt deleted file mode 100644 index a59a2cd5..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeSummaryRepository.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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.github.wulkanowy.data.repositories.local.GradeSummaryLocal -import io.github.wulkanowy.data.repositories.remote.GradeSummaryRemote -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/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt deleted file mode 100644 index a2a477b5..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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.data.repositories.local.HomeworkLocal -import io.github.wulkanowy.data.repositories.remote.HomeworkRemote -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, date: LocalDate, forceRefresh: Boolean = false): Single> { - return local.getHomework(semester, date).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getHomework(semester, date) - else Single.error(UnknownHostException()) - }.flatMap { newGrades -> - local.getHomework(semester, date).toSingle(emptyList()) - .doOnSuccess { oldGrades -> - local.deleteHomework(oldGrades - newGrades) - local.saveHomework(newGrades - oldGrades) - } - }.flatMap { local.getHomework(semester, date).toSingle(emptyList()) }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessagesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessagesRepository.kt deleted file mode 100644 index 06a0f6f9..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessagesRepository.kt +++ /dev/null @@ -1,79 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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.Message -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.local.MessagesLocal -import io.github.wulkanowy.data.repositories.remote.MessagesRemote -import io.reactivex.Completable -import io.reactivex.Single -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class MessagesRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: MessagesLocal, - private val remote: MessagesRemote -) { - - enum class MessageFolder(val id: Int = 1) { - RECEIVED(1), - SENT(2), - TRASHED(3) - } - - fun getMessages(studentId: Int, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): Single> { - return local.getMessages(studentId, folder).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getMessages(studentId, folder) - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getMessages(studentId, folder).toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteMessages(old - new) - local.saveMessages((new - old) - .onEach { - it.isNotified = !notify - }) - } - }.flatMap { local.getMessages(studentId, folder).toSingle(emptyList()) } - ) - } - - fun getMessage(studentId: Int, messageId: Int, markAsRead: Boolean = false): Single { - return local.getMessage(studentId, messageId) - .filter { !it.content.isNullOrEmpty() } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) local.getMessage(studentId, messageId).toSingle() - else Single.error(UnknownHostException()) - } - .flatMap { dbMessage -> - remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess { - local.updateMessage(dbMessage.copy(unread = false).apply { - id = dbMessage.id - content = it - }) - } - }.flatMap { - local.getMessage(studentId, messageId).toSingle() - } - ) - } - - fun getNewMessages(student: Student): Single> { - return local.getNewMessages(student).toSingle(emptyList()) - } - - fun updateMessage(message: Message): Completable { - return Completable.fromCallable { local.updateMessage(message) } - } - - fun updateMessages(messages: List): Completable { - return Completable.fromCallable { local.updateMessages(messages) } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt deleted file mode 100644 index 2836894f..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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.repositories.local.NoteLocal -import io.github.wulkanowy.data.repositories.remote.NoteRemote -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(semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Single> { - return local.getNotes(semester).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getNotes(semester) - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getNotes(semester).toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteNotes(old - new) - local.saveNotes((new - old) - .onEach { - if (notify) it.isNotified = false - }) - } - }.flatMap { local.getNotes(semester).toSingle(emptyList()) } - ) - } - - fun getNewNotes(semester: Semester): Single> { - return local.getNewNotes(semester).toSingle(emptyList()) - } - - fun updateNote(note: Note): Completable { - return local.updateNote(note) - } - - fun updateNotes(notes: List): Completable { - return local.updateNotes(notes) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt deleted file mode 100644 index 205baa3a..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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")?.toInt() ?: 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")?.toInt() ?: 1 - - 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 serviceEnablesKey: String = context.getString(R.string.pref_key_services_enable) - val isServiceEnabled: Boolean - get() = sharedPref.getBoolean(serviceEnablesKey, true) - - val servicesIntervalKey: String = context.getString(R.string.pref_key_services_interval) - val servicesInterval: Int - get() = sharedPref.getString(servicesIntervalKey, "60")?.toInt() ?: 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) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt deleted file mode 100644 index b7902106..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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.github.wulkanowy.data.repositories.local.SemesterLocal -import io.github.wulkanowy.data.repositories.remote.SemesterRemote -import io.reactivex.Maybe -import io.reactivex.Single -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()) - }.map { newSemesters -> - local.apply { - saveSemesters(newSemesters) - setCurrentSemester(newSemesters.single { it.isCurrent }) - } - }.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/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt deleted file mode 100644 index 97210da0..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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.repositories.local.StudentLocal -import io.github.wulkanowy.data.repositories.remote.StudentRemote -import io.reactivex.Completable -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 -) { - - val isStudentSaved - get() = local.isStudentSaved - - lateinit var cachedStudents: Single> - private set - - fun getStudents(email: String, password: String, symbol: String, endpoint: String): Single> { - cachedStudents = ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - apiHelper.initApi(email, password, symbol, endpoint) - if (it) remote.getStudents(email, password, endpoint) - else Single.error(UnknownHostException("No internet connection")) - }.doOnSuccess { cachedStudents = Single.just(it) } - return cachedStudents - } - - fun getSavedStudents(decryptPass: Boolean = true): Single> { - return local.getStudents(decryptPass).toSingle(emptyList()) - } - - fun getCurrentStudent(decryptPass: Boolean = true): Single { - return local.getCurrentStudent(decryptPass).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/SubjectRepostory.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepostory.kt deleted file mode 100644 index e3e8a2a7..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepostory.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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.github.wulkanowy.data.repositories.local.SubjectLocal -import io.github.wulkanowy.data.repositories.remote.SubjectRemote -import io.reactivex.Single -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SubjectRepostory @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/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt deleted file mode 100644 index 6cc6a043..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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.data.repositories.local.TimetableLocal -import io.github.wulkanowy.data.repositories.remote.TimetableRemote -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, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false) - : Single> { - return Single.fromCallable { startDate.monday to endDate.friday } - .flatMap { dates -> - local.getTimetable(semester, dates.first, dates.second).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getTimetable(semester, dates.first, dates.second) - else Single.error(UnknownHostException()) - }.flatMap { newTimetable -> - local.getTimetable(semester, dates.first, dates.second) - .toSingle(emptyList()) - .doOnSuccess { oldTimetable -> - local.deleteTimetable(oldTimetable - newTimetable) - local.saveTimetable(newTimetable - oldTimetable) - } - }.flatMap { - local.getTimetable(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/local/AttendanceLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceLocal.kt deleted file mode 100644 index 9a318dba..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceLocal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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 - -class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDao) { - - fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { - return attendanceDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate) - .filter { !it.isEmpty() } - } - - fun saveAttendance(attendance: List) { - attendanceDb.insertAll(attendance) - } - - fun deleteAttendance(attendance: List) { - attendanceDb.deleteAll(attendance) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceSummaryLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceSummaryLocal.kt deleted file mode 100644 index 2bb9f122..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceSummaryLocal.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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 - -class AttendanceSummaryLocal @Inject constructor(private val attendanceDb: AttendanceSummaryDao) { - - fun getAttendanceSummary(semester: Semester, subjectId: Int): Maybe> { - return attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId).filter { !it.isEmpty() } - } - - fun saveAttendanceSummary(attendance: List) { - attendanceDb.insertAll(attendance) - } - - fun deleteAttendanceSummary(attendance: List) { - attendanceDb.deleteAll(attendance) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/ExamLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/ExamLocal.kt deleted file mode 100644 index 3e32a635..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/ExamLocal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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 - -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/local/GradeLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/GradeLocal.kt deleted file mode 100644 index d6d46340..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/GradeLocal.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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.Completable -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeLocal @Inject constructor(private val gradeDb: GradeDao) { - - fun getGrades(semester: Semester): Maybe> { - return gradeDb.loadAll(semester.semesterId, semester.studentId).filter { !it.isEmpty() } - } - - fun getNewGrades(semester: Semester): Maybe> { - return gradeDb.loadAllNew(semester.semesterId, semester.studentId) - } - - fun saveGrades(grades: List) { - gradeDb.insertAll(grades) - } - - fun updateGrade(grade: Grade): Completable { - return Completable.fromCallable { gradeDb.update(grade) } - } - - fun updateGrades(grades: List): Completable { - return Completable.fromCallable { gradeDb.updateAll(grades) } - } - - fun deleteGrades(grades: List) { - gradeDb.deleteAll(grades) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/GradeSummaryLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/GradeSummaryLocal.kt deleted file mode 100644 index 6a72416d..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/GradeSummaryLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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 getGradesSummary(semester: Semester): Maybe> { - return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId) - .filter { !it.isEmpty() } - } - - fun saveGradesSummary(gradesSummary: List) { - gradeSummaryDb.insertAll(gradesSummary) - } - - fun deleteGradesSummary(gradesSummary: List) { - gradeSummaryDb.deleteAll(gradesSummary) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/HomeworkLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/HomeworkLocal.kt deleted file mode 100644 index ea18d814..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/HomeworkLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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 getHomework(semester: Semester, date: LocalDate): Maybe> { - return homeworkDb.loadAll(semester.semesterId, semester.studentId, date).filter { !it.isEmpty() } - } - - fun saveHomework(homework: List) { - homeworkDb.insertAll(homework) - } - - fun deleteHomework(homework: List) { - homeworkDb.deleteAll(homework) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/MessagesLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/MessagesLocal.kt deleted file mode 100644 index 531cd8b0..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/MessagesLocal.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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.MessagesRepository -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class MessagesLocal @Inject constructor(private val messagesDb: MessagesDao) { - - fun getMessage(studentId: Int, id: Int): Maybe { - return messagesDb.loadOne(studentId, id) - } - - fun getMessages(studentId: Int, folder: MessagesRepository.MessageFolder): Maybe> { - return when (folder) { - MessagesRepository.MessageFolder.TRASHED -> messagesDb.loadDeleted(studentId) - else -> messagesDb.load(studentId, folder.id) - }.filter { !it.isEmpty() } - } - - fun getNewMessages(student: Student): Maybe> { - return messagesDb.loadNewMessages(student.studentId) - } - - fun saveMessages(messages: List): List { - return messagesDb.insertAll(messages) - } - - fun updateMessage(message: Message) { - return messagesDb.update(message) - } - - fun updateMessages(messages: List) { - return messagesDb.updateAll(messages) - } - - fun deleteMessages(messages: List) { - messagesDb.deleteAll(messages) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/NoteLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/NoteLocal.kt deleted file mode 100644 index 543eab9b..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/NoteLocal.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -import io.github.wulkanowy.data.db.dao.NoteDao -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Completable -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class NoteLocal @Inject constructor(private val noteDb: NoteDao) { - - fun getNotes(semester: Semester): Maybe> { - return noteDb.loadAll(semester.semesterId, semester.studentId).filter { !it.isEmpty() } - } - - fun getNewNotes(semester: Semester): Maybe> { - return noteDb.loadNew(semester.semesterId, semester.studentId) - } - - fun saveNotes(notes: List) { - noteDb.insertAll(notes) - } - - fun updateNote(note: Note): Completable { - return Completable.fromCallable { noteDb.update(note) } - } - - fun updateNotes(notes: List): Completable { - return Completable.fromCallable { noteDb.updateAll(notes) } - } - - fun deleteNotes(notes: List) { - noteDb.deleteAll(notes) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/SemesterLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/SemesterLocal.kt deleted file mode 100644 index 77000478..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/SemesterLocal.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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) { - return semesterDb.insertAll(semesters) - } - - fun getSemesters(student: Student): Maybe> { - return semesterDb.loadAll(student.studentId).filter { !it.isEmpty() } - } - - fun setCurrentSemester(semester: Semester) { - semesterDb.run { - resetCurrent(semester.studentId) - updateCurrent(semester.semesterId, semester.diaryId) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/StudentLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/StudentLocal.kt deleted file mode 100644 index 0e4e9cbd..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/StudentLocal.kt +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -import android.content.Context -import io.github.wulkanowy.data.db.SharedPrefHelper -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 sharedPref: SharedPrefHelper, - private val context: Context -) { - - companion object { - const val STUDENT_SAVED_KEY: String = "is_student_saved" - } - - val isStudentSaved - get() = sharedPref.getBoolean(STUDENT_SAVED_KEY, false) - - fun saveStudent(student: Student): Single { - return Single.fromCallable { studentDb.insert(student.copy(password = encrypt(student.password, context))) } - .doOnSuccess { sharedPref.putBoolean(STUDENT_SAVED_KEY, true) } - } - - fun getStudents(decryptPass: Boolean): Maybe> { - return studentDb.loadAll() - .map { list -> list.map { it.apply { if (decryptPass) password = decrypt(password) } } } - } - - 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.studentId) - } - }.doOnComplete { sharedPref.putBoolean(STUDENT_SAVED_KEY, true) } - } - - fun logoutStudent(student: Student): Completable { - return Completable.fromCallable { - studentDb.delete(student) - if (student.isCurrent) sharedPref.putBoolean(STUDENT_SAVED_KEY, false) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/SubjectLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/SubjectLocal.kt deleted file mode 100644 index 7ca7c1b0..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/SubjectLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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/local/TimetableLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/TimetableLocal.kt deleted file mode 100644 index 63cc7c94..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/TimetableLocal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -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 - -class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao) { - - fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { - return timetableDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate) - .filter { !it.isEmpty() } - } - - fun saveTimetable(timetables: List) { - timetableDb.insertAll(timetables) - } - - fun deleteTimetable(timetables: List) { - timetableDb.deleteAll(timetables) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemote.kt deleted file mode 100644 index 79047606..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemote.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.data.repositories.remote - -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/remote/AttendanceSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceSummaryRemote.kt deleted file mode 100644 index 079eb4b8..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceSummaryRemote.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.repositories.remote - -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/remote/ExamRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/ExamRemote.kt deleted file mode 100644 index 3937b768..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/ExamRemote.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.data.repositories.remote - -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 - -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/remote/GradeRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/GradeRemote.kt deleted file mode 100644 index df3bf4d2..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/GradeRemote.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.data.repositories.remote - -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 ?: "", - description = it.description, - 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/remote/GradeSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/GradeSummaryRemote.kt deleted file mode 100644 index af45ca73..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/GradeSummaryRemote.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.repositories.remote - -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/remote/HomeworkRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/HomeworkRemote.kt deleted file mode 100644 index 0fdc0251..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/HomeworkRemote.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.repositories.remote - -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, date: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getHomework(date, date) } - .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/remote/MessagesRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/MessagesRemote.kt deleted file mode 100644 index bc0f3400..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/MessagesRemote.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.data.repositories.remote - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.api.messages.Folder -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.repositories.MessagesRepository -import io.github.wulkanowy.utils.toLocalDateTime -import io.reactivex.Single -import javax.inject.Inject -import io.github.wulkanowy.api.messages.Message as ApiMessage - -class MessagesRemote @Inject constructor(private val api: Api) { - - fun getMessages(studentId: Int, folder: MessagesRepository.MessageFolder): Single> { - return api.getMessages(Folder.valueOf(folder.name)).map { messages -> - messages.map { - Message( - studentId = studentId, - realId = it.id, - messageId = it.messageId, - sender = it.sender, - senderId = it.senderId, - recipient = it.recipient, - recipientId = it.recipientId, - subject = it.subject.trim(), - date = it.date?.toLocalDateTime(), - folderId = it.folderId, - unread = it.unread, - unreadBy = it.unreadBy, - readBy = it.readBy, - removed = it.removed - ) - } - } - } - - fun getMessagesContent(message: Message, markAsRead: Boolean = false): Single { - return api.getMessageContent(message.messageId ?: 0, message.folderId, markAsRead, message.realId ?: 0) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/NoteRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/NoteRemote.kt deleted file mode 100644 index 31f28e11..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/NoteRemote.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.data.repositories.remote - -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( - semesterId = semester.semesterId, - 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/remote/SemesterRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/SemesterRemote.kt deleted file mode 100644 index 1cf10501..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/SemesterRemote.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.data.repositories.remote - -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 - ) - } - - } - } -} - - diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/StudentRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/StudentRemote.kt deleted file mode 100644 index 6c6e26a9..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/StudentRemote.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.repositories.remote - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Single -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.getPupils().map { students -> - students.map { pupil -> - Student( - email = email, - password = password, - symbol = pupil.symbol, - studentId = pupil.studentId, - studentName = pupil.studentName, - schoolSymbol = pupil.schoolSymbol, - schoolName = pupil.description, - endpoint = endpoint, - loginType = pupil.loginType.name - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/SubjectRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/SubjectRemote.kt deleted file mode 100644 index ae565d48..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/SubjectRemote.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.data.repositories.remote - -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/remote/TimetableRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/TimetableRemote.kt deleted file mode 100644 index aa8949fd..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/TimetableRemote.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.data.repositories.remote - -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 - -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, - group = it.group, - room = it.room, - teacher = it.teacher, - info = it.info, - changes = it.changes, - canceled = it.canceled - ) - } - } - } -} 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 00000000..43b37b7e --- /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.login.NotLoggedInErrorException; + +public interface SyncContract { + + void sync() throws NotLoggedInErrorException, IOException, ParseException; +} 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 00000000..e0b38884 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/grades/GradeSync.java @@ -0,0 +1,72 @@ +package io.github.wulkanowy.data.sync.grades; + +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.github.wulkanowy.api.Vulcan; +import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.data.db.dao.entities.Account; +import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.dao.entities.Grade; +import io.github.wulkanowy.data.db.dao.entities.SubjectDao; +import io.github.wulkanowy.data.db.shared.SharedPrefContract; +import io.github.wulkanowy.data.sync.SyncContract; +import io.github.wulkanowy.utils.DataObjectConverter; +import io.github.wulkanowy.utils.EntitiesCompare; +import io.github.wulkanowy.utils.LogUtils; + +@Singleton +public class GradeSync implements SyncContract { + + private final DaoSession daoSession; + + private final Vulcan vulcan; + + private final SharedPrefContract sharedPref; + + @Inject + GradeSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) { + this.daoSession = daoSession; + this.sharedPref = sharedPref; + this.vulcan = vulcan; + } + + @Override + public void sync() throws IOException, NotLoggedInErrorException, ParseException { + + long userId = sharedPref.getCurrentUserId(); + + Account account = daoSession.getAccountDao().load(userId); + account.resetGradeList(); + account.resetSubjectList(); + + List gradesFromNet = DataObjectConverter + .gradesToGradeEntities(vulcan.getGradesList().getAll()); + List gradesFromDb = account.getGradeList(); + + List updatedGrades = EntitiesCompare.compareGradeList(gradesFromNet, gradesFromDb); + + daoSession.getGradeDao().deleteInTx(gradesFromDb); + + List lastList = new ArrayList<>(); + + for (Grade grade : updatedGrades) { + grade.setUserId(userId); + grade.setSubjectId(daoSession.getSubjectDao().queryBuilder() + .where(SubjectDao.Properties.Name.eq(grade.getSubject()), + SubjectDao.Properties.UserId.eq(userId)) + .build() + .uniqueOrThrow().getId()); + lastList.add(grade); + } + + daoSession.getGradeDao().insertInTx(lastList); + + LogUtils.debug("Synchronization grades (amount = " + lastList.size() + ")"); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/login/LoginSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/login/LoginSync.java new file mode 100644 index 00000000..71838ff5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/login/LoginSync.java @@ -0,0 +1,81 @@ +package io.github.wulkanowy.data.sync.login; + +import android.content.Context; + +import java.io.IOException; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.github.wulkanowy.api.Vulcan; +import io.github.wulkanowy.api.login.AccountPermissionException; +import io.github.wulkanowy.api.login.BadCredentialsException; +import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.api.login.VulcanOfflineException; +import io.github.wulkanowy.data.db.dao.entities.Account; +import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.shared.SharedPrefContract; +import io.github.wulkanowy.di.annotations.ApplicationContext; +import io.github.wulkanowy.utils.LogUtils; +import io.github.wulkanowy.utils.security.CryptoException; +import io.github.wulkanowy.utils.security.Scrambler; + +@Singleton +public class LoginSync implements LoginSyncContract { + + private final DaoSession daoSession; + + private final SharedPrefContract sharedPref; + + private final Vulcan vulcan; + + private final Context context; + + @Inject + LoginSync(DaoSession daoSession, SharedPrefContract sharedPref, + Vulcan vulcan, @ApplicationContext Context context) { + this.daoSession = daoSession; + this.sharedPref = sharedPref; + this.vulcan = vulcan; + this.context = context; + } + + @Override + public void loginUser(String email, String password, String symbol) + throws NotLoggedInErrorException, AccountPermissionException, IOException, + CryptoException, VulcanOfflineException, BadCredentialsException { + + LogUtils.debug("Login new user email=" + email); + + vulcan.login(email, password, symbol); + + Account account = new Account() + .setName(vulcan.getBasicInformation().getPersonalData().getFirstAndLastName()) + .setEmail(email) + .setPassword(Scrambler.encrypt(email, password, context)) + .setSymbol(vulcan.getSymbol()) + .setSnpId(vulcan.getStudentAndParent().getId()); + + sharedPref.setCurrentUserId(daoSession.getAccountDao().insert(account)); + } + + @Override + public void loginCurrentUser() throws NotLoggedInErrorException, AccountPermissionException, + IOException, CryptoException, VulcanOfflineException, BadCredentialsException { + + long userId = sharedPref.getCurrentUserId(); + + if (userId == 0) { + throw new IOException("Can't find logged user"); + } + + LogUtils.debug("Login current user id=" + userId); + + Account account = daoSession.getAccountDao().load(userId); + + vulcan.login(account.getEmail(), + Scrambler.decrypt(account.getEmail(), account.getPassword()), + account.getSymbol(), + account.getSnpId()); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/login/LoginSyncContract.java b/app/src/main/java/io/github/wulkanowy/data/sync/login/LoginSyncContract.java new file mode 100644 index 00000000..1aed8b1b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/login/LoginSyncContract.java @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.sync.login; + +import java.io.IOException; + +import io.github.wulkanowy.api.login.AccountPermissionException; +import io.github.wulkanowy.api.login.BadCredentialsException; +import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.api.login.VulcanOfflineException; +import io.github.wulkanowy.utils.security.CryptoException; + +public interface LoginSyncContract { + + void loginUser(String email, String password, String symbol) + throws NotLoggedInErrorException, AccountPermissionException, IOException, + CryptoException, VulcanOfflineException, BadCredentialsException; + + void loginCurrentUser() throws NotLoggedInErrorException, AccountPermissionException, IOException, + CryptoException, VulcanOfflineException, BadCredentialsException; +} 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 00000000..d888090b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/subjects/SubjectSync.java @@ -0,0 +1,61 @@ +package io.github.wulkanowy.data.sync.subjects; + +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.github.wulkanowy.api.Vulcan; +import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.dao.entities.Subject; +import io.github.wulkanowy.data.db.dao.entities.SubjectDao; +import io.github.wulkanowy.data.db.shared.SharedPrefContract; +import io.github.wulkanowy.data.sync.SyncContract; +import io.github.wulkanowy.utils.DataObjectConverter; +import io.github.wulkanowy.utils.LogUtils; + +@Singleton +public class SubjectSync implements SyncContract { + + private final SubjectDao subjectDao; + + private final Vulcan vulcan; + + private final SharedPrefContract sharedPref; + + @Inject + SubjectSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) { + this.subjectDao = daoSession.getSubjectDao(); + this.sharedPref = sharedPref; + this.vulcan = vulcan; + } + + @Override + public void sync() throws NotLoggedInErrorException, IOException, ParseException { + + long userId = sharedPref.getCurrentUserId(); + + List subjectsFromNet = DataObjectConverter + .subjectsToSubjectEntities(vulcan.getSubjectsList().getAll()); + + subjectDao.deleteInTx(subjectDao.queryBuilder() + .where(SubjectDao.Properties.UserId.eq(userId)) + .build() + .list()); + + List lastList = new ArrayList<>(); + + for (Subject subject : subjectsFromNet) { + subject.setUserId(userId); + lastList.add(subject); + } + + subjectDao.insertInTx(lastList); + + LogUtils.debug("Synchronization subjects (amount = " + lastList.size() + ")"); + } +} 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 00000000..dcb048f1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/timetable/TimetableSync.java @@ -0,0 +1,120 @@ +package io.github.wulkanowy.data.sync.timetable; + +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.github.wulkanowy.api.Vulcan; +import io.github.wulkanowy.api.login.NotLoggedInErrorException; +import io.github.wulkanowy.data.db.dao.entities.DaoSession; +import io.github.wulkanowy.data.db.dao.entities.Day; +import io.github.wulkanowy.data.db.dao.entities.DayDao; +import io.github.wulkanowy.data.db.dao.entities.Lesson; +import io.github.wulkanowy.data.db.dao.entities.LessonDao; +import io.github.wulkanowy.data.db.dao.entities.Week; +import io.github.wulkanowy.data.db.dao.entities.WeekDao; +import io.github.wulkanowy.data.db.shared.SharedPrefContract; +import io.github.wulkanowy.utils.DataObjectConverter; +import io.github.wulkanowy.utils.LogUtils; +import io.github.wulkanowy.utils.TimeUtils; + +@Singleton +public class TimetableSync implements TimetableSyncContract { + + private final DaoSession daoSession; + + private final Vulcan vulcan; + + private final SharedPrefContract sharedPref; + + @Inject + TimetableSync(DaoSession daoSession, SharedPrefContract sharedPref, Vulcan vulcan) { + this.daoSession = daoSession; + this.sharedPref = sharedPref; + this.vulcan = vulcan; + } + + @Override + public void syncTimetable(String date) throws NotLoggedInErrorException, IOException, ParseException { + long userId = sharedPref.getCurrentUserId(); + + io.github.wulkanowy.api.generic.Week weekFromNet = + date == null ? vulcan.getTimetable().getWeekTable() + : vulcan.getTimetable().getWeekTable(String.valueOf(TimeUtils.getNetTicks(date))); + + Week weekFromDb = daoSession.getWeekDao().queryBuilder() + .where(WeekDao.Properties.UserId.eq(userId), + WeekDao.Properties.StartDayDate.eq(weekFromNet.getStartDayDate())) + .unique(); + + Long weekId; + + if (weekFromDb == null) { + Week weekFromNetEntity = DataObjectConverter.weekToWeekEntity(weekFromNet).setUserId(userId); + weekId = daoSession.getWeekDao().insert(weekFromNetEntity); + } else { + weekId = weekFromDb.getId(); + } + + List dayListFromNet = weekFromNet.getDays(); + + List updatedLessonList = new ArrayList<>(); + + for (io.github.wulkanowy.api.generic.Day dayFromNet : dayListFromNet) { + Day dayFromNetEntity = DataObjectConverter.dayToDayEntity(dayFromNet); + + Day dayFromDb = daoSession.getDayDao().queryBuilder() + .where(DayDao.Properties.UserId.eq(userId), + DayDao.Properties.WeekId.eq(weekId), + DayDao.Properties.Date.eq(dayFromNetEntity.getDate())) + .unique(); + + dayFromNetEntity.setUserId(userId); + dayFromNetEntity.setWeekId(weekId); + + Long dayId; + + if (dayFromDb != null) { + dayFromNetEntity.setId(dayFromDb.getId()); + daoSession.getDayDao().save(dayFromNetEntity); + dayId = dayFromNetEntity.getId(); + } else { + dayId = daoSession.getDayDao().insert(dayFromNetEntity); + } + + List lessonListFromNetEntities = DataObjectConverter + .lessonsToLessonsEntities(dayFromNet.getLessons()); + + for (Lesson lessonFromNetEntity : lessonListFromNetEntities) { + Lesson lessonFromDb = daoSession.getLessonDao().queryBuilder() + .where(LessonDao.Properties.DayId.eq(dayId), + LessonDao.Properties.Date.eq(lessonFromNetEntity.getDate()), + LessonDao.Properties.StartTime.eq(lessonFromNetEntity.getStartTime()), + LessonDao.Properties.EndTime.eq(lessonFromNetEntity.getEndTime())) + .unique(); + + if (lessonFromDb != null) { + lessonFromNetEntity.setId(lessonFromDb.getId()); + } + + lessonFromNetEntity.setDayId(dayFromNetEntity.getId()); + + if (!"".equals(lessonFromNetEntity.getSubject())) { + updatedLessonList.add(lessonFromNetEntity); + } + } + } + daoSession.getLessonDao().saveInTx(updatedLessonList); + + LogUtils.debug("Synchronization lessons (amount = " + updatedLessonList.size() + ")"); + } + + @Override + public void syncTimetable() throws NotLoggedInErrorException, IOException, ParseException { + syncTimetable(null); + } +} 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 00000000..4c37d2ec --- /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.login.NotLoggedInErrorException; + +public interface TimetableSyncContract { + + void syncTimetable(String date) throws NotLoggedInErrorException, IOException, ParseException; + + void syncTimetable() throws NotLoggedInErrorException, 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 21c193e5..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/AppComponent.kt +++ /dev/null @@ -1,20 +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 javax.inject.Singleton - -@Singleton -@Component(modules = [ - AndroidSupportInjectionModule::class, - AppModule::class, - RepositoryModule::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 fc2e9297..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.di - -import android.content.Context -import com.firebase.jobdispatcher.FirebaseJobDispatcher -import com.firebase.jobdispatcher.GooglePlayDriver -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.WulkanowyApp -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -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 provideJobDispatcher(context: Context) = FirebaseJobDispatcher(GooglePlayDriver(context)) - - @Singleton - @Provides - fun provideFirebaseAnalyticsHelper(context: Context) = FirebaseAnalyticsHelper(FirebaseAnalytics.getInstance(context)) -} 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 9f4ecb80..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt +++ /dev/null @@ -1,38 +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.services.job.SyncWorker -import io.github.wulkanowy.services.widgets.TimetableWidgetService -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.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 bindTimetableWidgetService(): TimetableWidgetService - - @ContributesAndroidInjector - abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider - - @ContributesAndroidInjector - abstract fun bindSyncJob(): SyncWorker -} 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 00000000..2a74c32d --- /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 00000000..04139d99 --- /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 00000000..fabcefba --- /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 00000000..f103994a --- /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 00000000..98f364f7 --- /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 00000000..919c77a0 --- /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 00000000..90e6c02f --- /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 00000000..81d351be --- /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 00000000..3365a317 --- /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 00000000..3439a949 --- /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 00000000..01a0fc02 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/component/FragmentComponent.java @@ -0,0 +1,25 @@ +package io.github.wulkanowy.di.component; + +import dagger.Component; +import io.github.wulkanowy.di.annotations.PerFragment; +import io.github.wulkanowy.di.modules.FragmentModule; +import io.github.wulkanowy.ui.main.attendance.AttendanceFragment; +import io.github.wulkanowy.ui.main.dashboard.DashboardFragment; +import io.github.wulkanowy.ui.main.grades.GradesFragment; +import io.github.wulkanowy.ui.main.timetable.TimetableFragment; +import io.github.wulkanowy.ui.main.timetable.TimetableTabFragment; + +@PerFragment +@Component(dependencies = ApplicationComponent.class, modules = FragmentModule.class) +public interface FragmentComponent { + + void inject(GradesFragment gradesFragment); + + void inject(AttendanceFragment attendanceFragment); + + void inject(DashboardFragment dashboardFragment); + + void inject(TimetableFragment timetableFragment); + + void inject(TimetableTabFragment timetableTabFragment); +} 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 00000000..53dc1086 --- /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 00000000..58e9909b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/modules/ApplicationModule.java @@ -0,0 +1,129 @@ +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.grades.GradeSync; +import io.github.wulkanowy.data.sync.login.LoginSync; +import io.github.wulkanowy.data.sync.login.LoginSyncContract; +import io.github.wulkanowy.data.sync.subjects.SubjectSync; +import io.github.wulkanowy.data.sync.timetable.TimetableSync; +import io.github.wulkanowy.data.sync.timetable.TimetableSyncContract; +import io.github.wulkanowy.di.annotations.ApplicationContext; +import io.github.wulkanowy.di.annotations.DatabaseInfo; +import io.github.wulkanowy.di.annotations.SharedPreferencesInfo; +import io.github.wulkanowy.di.annotations.SyncGrades; +import io.github.wulkanowy.di.annotations.SyncSubjects; +import io.github.wulkanowy.utils.AppConstant; + +@Module +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 + LoginSyncContract provideLoginSync(LoginSync loginSync) { + return loginSync; + } + + @SyncGrades + @Singleton + @Provides + SyncContract provideGradesSync(GradeSync gradeSync) { + return gradeSync; + } + + @SyncSubjects + @Singleton + @Provides + SyncContract provideSubjectSync(SubjectSync subjectSync) { + return subjectSync; + } + + @Singleton + @Provides + TimetableSyncContract provideTimetableSync(TimetableSync timetableSync) { + return timetableSync; + } + + @Provides + FirebaseJobDispatcher provideDispatcher() { + return new FirebaseJobDispatcher(new GooglePlayDriver(application)); + } +} 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 00000000..8999bca8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/modules/FragmentModule.java @@ -0,0 +1,76 @@ +package io.github.wulkanowy.di.modules; + +import android.support.v4.app.Fragment; + +import dagger.Module; +import dagger.Provides; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import io.github.wulkanowy.di.annotations.PerFragment; +import io.github.wulkanowy.ui.main.attendance.AttendanceContract; +import io.github.wulkanowy.ui.main.attendance.AttendancePresenter; +import io.github.wulkanowy.ui.main.dashboard.DashboardContract; +import io.github.wulkanowy.ui.main.dashboard.DashboardPresenter; +import io.github.wulkanowy.ui.main.grades.GradeHeaderItem; +import io.github.wulkanowy.ui.main.grades.GradesContract; +import io.github.wulkanowy.ui.main.grades.GradesPresenter; +import io.github.wulkanowy.ui.main.timetable.TimetableContract; +import io.github.wulkanowy.ui.main.timetable.TimetableHeaderItem; +import io.github.wulkanowy.ui.main.timetable.TimetablePagerAdapter; +import io.github.wulkanowy.ui.main.timetable.TimetablePresenter; +import io.github.wulkanowy.ui.main.timetable.TimetableTabContract; +import io.github.wulkanowy.ui.main.timetable.TimetableTabPresenter; + +@Module +public class FragmentModule { + + private final Fragment fragment; + + public FragmentModule(Fragment fragment) { + this.fragment = fragment; + } + + @PerFragment + @Provides + GradesContract.Presenter provideGradesPresenter(GradesPresenter gradesPresenter) { + return gradesPresenter; + } + + @PerFragment + @Provides + AttendanceContract.Presenter provideAttendancePresenter(AttendancePresenter attendancePresenter) { + return attendancePresenter; + } + + @PerFragment + @Provides + DashboardContract.Presenter provideDashboardPresenter(DashboardPresenter dashboardPresenter) { + return dashboardPresenter; + } + + @PerFragment + @Provides + TimetableContract.Presenter provideTimetablePresenter(TimetablePresenter timetablePresenter) { + return timetablePresenter; + } + + @PerFragment + @Provides + TimetableTabContract.Presenter provideTimetableTabPresenter(TimetableTabPresenter timetableTabPresenter) { + return timetableTabPresenter; + } + + @Provides + TimetablePagerAdapter provideTimetablePagerAdapter() { + return new TimetablePagerAdapter(fragment.getChildFragmentManager()); + } + + @Provides + FlexibleAdapter provideGradesAdapter() { + return new FlexibleAdapter<>(null); + } + + @Provides + FlexibleAdapter provideTimetableTabAdapter() { + 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 c1b4352a..00000000 --- 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 08884dac..00000000 --- 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 1203d7d8..00000000 --- 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 00000000..ab721f46 --- /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/SyncJob.java b/app/src/main/java/io/github/wulkanowy/services/SyncJob.java new file mode 100644 index 00000000..ab26fcee --- /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.loginCurrentUser(); + repository.syncAll(); + + gradeList = repository.getNewGrades(); + + if (!gradeList.isEmpty()) { + showNotification(); + } + return JobService.RESULT_SUCCESS; + } catch (Exception e) { + LogUtils.error("During background synchronization an error occurred", e); + return JobService.RESULT_FAIL_RETRY; + } + } + + private void showNotification() { + NotificationService service = new NotificationService(getApplicationContext()); + + service.notify(service.notificationBuilder() + .setContentTitle(getStringTitle()) + .setContentText(getStringContent()) + .setSmallIcon(R.drawable.ic_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/job/ServiceHelper.kt b/app/src/main/java/io/github/wulkanowy/services/job/ServiceHelper.kt deleted file mode 100644 index d81b4b6d..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/job/ServiceHelper.kt +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.wulkanowy.services.job - -import com.firebase.jobdispatcher.Constraint.ON_ANY_NETWORK -import com.firebase.jobdispatcher.Constraint.ON_UNMETERED_NETWORK -import com.firebase.jobdispatcher.FirebaseJobDispatcher -import com.firebase.jobdispatcher.Lifetime.FOREVER -import com.firebase.jobdispatcher.RetryStrategy.DEFAULT_EXPONENTIAL -import com.firebase.jobdispatcher.Trigger.executionWindow -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.utils.isHolidays -import org.threeten.bp.LocalDate -import timber.log.Timber -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ServiceHelper @Inject constructor( - private val prefRepository: PreferencesRepository, - private val dispatcher: FirebaseJobDispatcher -) { - - fun reloadFullSyncService() { - startFullSyncService(true) - } - - fun startFullSyncService(replaceCurrent: Boolean = false) { - if (LocalDate.now().isHolidays || !prefRepository.isServiceEnabled) { - Timber.d("Services disabled or it's holidays") - return - } - - dispatcher.mustSchedule( - dispatcher.newJobBuilder() - .setLifetime(FOREVER) - .setRecurring(true) - .setService(SyncWorker::class.java) - .setTag(SyncWorker.WORK_TAG) - .setTrigger( - executionWindow( - prefRepository.servicesInterval * 60, - (prefRepository.servicesInterval + 10) * 60 - ) - ) - .setConstraints(if (prefRepository.isServicesOnlyWifi) ON_UNMETERED_NETWORK else ON_ANY_NETWORK) - .setReplaceCurrent(replaceCurrent) - .setRetryStrategy(DEFAULT_EXPONENTIAL) - .build() - ) - - Timber.d("Services started") - } - - fun stopFullSyncService() { - dispatcher.cancel(SyncWorker.WORK_TAG) - Timber.d("Services stopped") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/job/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/job/SyncWorker.kt deleted file mode 100644 index 65fb3abc..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/job/SyncWorker.kt +++ /dev/null @@ -1,174 +0,0 @@ -package io.github.wulkanowy.services.job - -import com.firebase.jobdispatcher.JobParameters -import com.firebase.jobdispatcher.SimpleJobService -import dagger.android.AndroidInjection -import io.github.wulkanowy.data.repositories.AttendanceRepository -import io.github.wulkanowy.data.repositories.ExamRepository -import io.github.wulkanowy.data.repositories.GradeRepository -import io.github.wulkanowy.data.repositories.GradeSummaryRepository -import io.github.wulkanowy.data.repositories.HomeworkRepository -import io.github.wulkanowy.data.repositories.MessagesRepository -import io.github.wulkanowy.data.repositories.MessagesRepository.MessageFolder.RECEIVED -import io.github.wulkanowy.data.repositories.NoteRepository -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.repositories.TimetableRepository -import io.github.wulkanowy.services.notification.GradeNotification -import io.github.wulkanowy.services.notification.MessageNotification -import io.github.wulkanowy.services.notification.NoteNotification -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.isHolidays -import io.github.wulkanowy.utils.monday -import io.reactivex.Single -import io.reactivex.disposables.CompositeDisposable -import org.threeten.bp.LocalDate -import timber.log.Timber -import javax.inject.Inject - -class SyncWorker : SimpleJobService() { - - @Inject - lateinit var student: StudentRepository - - @Inject - lateinit var semester: SemesterRepository - - @Inject - lateinit var gradesDetails: GradeRepository - - @Inject - lateinit var gradesSummary: GradeSummaryRepository - - @Inject - lateinit var attendance: AttendanceRepository - - @Inject - lateinit var exam: ExamRepository - - @Inject - lateinit var timetable: TimetableRepository - - @Inject - lateinit var message: MessagesRepository - - @Inject - lateinit var note: NoteRepository - - @Inject - lateinit var homework: HomeworkRepository - - @Inject - lateinit var prefRepository: PreferencesRepository - - private val disposable = CompositeDisposable() - - companion object { - const val WORK_TAG = "FULL_SYNC" - } - - override fun onCreate() { - super.onCreate() - AndroidInjection.inject(this) - } - - override fun onRunJob(job: JobParameters?): Int { - Timber.d("Synchronization started") - - val start = LocalDate.now().monday - val end = LocalDate.now().friday - - if (start.isHolidays) return RESULT_FAIL_NORETRY - - var error: Throwable? = null - - disposable.add(student.getCurrentStudent() - .flatMap { semester.getCurrentSemester(it, true) } - .flatMapPublisher { - Single.merge( - listOf( - gradesDetails.getGrades(it, true, true), - gradesSummary.getGradesSummary(it, true), - attendance.getAttendance(it, start, end, true), - exam.getExams(it, start, end, true), - timetable.getTimetable(it, start, end, true), - message.getMessages(it.studentId, RECEIVED, true, true), - note.getNotes(it, true, true), - homework.getHomework(it, LocalDate.now(), true), - homework.getHomework(it, LocalDate.now().plusDays(1), true) - ) - ) - } - .subscribe({}, { error = it })) - - return if (null === error) { - if (prefRepository.isNotificationsEnable) sendNotifications() - Timber.d("Synchronization successful") - RESULT_SUCCESS - } else { - Timber.e(error, "Synchronization failed") - RESULT_FAIL_RETRY - } - } - - private fun sendNotifications() { - sendGradeNotifications() - sendMessageNotification() - sendNoteNotification() - } - - private fun sendGradeNotifications() { - disposable.add(student.getCurrentStudent() - .flatMap { semester.getCurrentSemester(it) } - .flatMap { gradesDetails.getNewGrades(it) } - .map { it.filter { grade -> !grade.isNotified } } - .doOnSuccess { - if (it.isNotEmpty()) { - Timber.d("Found ${it.size} unread grades") - GradeNotification(applicationContext).sendNotification(it) - } - } - .map { it.map { grade -> grade.apply { isNotified = true } } } - .flatMapCompletable { gradesDetails.updateGrades(it) } - .subscribe({}, { Timber.e(it, "Grade notifications sending failed") })) - } - - private fun sendMessageNotification() { - disposable.add(student.getCurrentStudent() - .flatMap { message.getNewMessages(it) } - .map { it.filter { message -> !message.isNotified } } - .doOnSuccess{ - if (it.isNotEmpty()) { - Timber.d("Found ${it.size} unread messages") - MessageNotification(applicationContext).sendNotification(it) - } - } - .map { it.map { message -> message.apply { isNotified = true } } } - .flatMapCompletable { message.updateMessages(it) } - .subscribe({}, { Timber.e(it, "Message notifications sending failed") }) - ) - } - - private fun sendNoteNotification() { - disposable.add(student.getCurrentStudent() - .flatMap { semester.getCurrentSemester(it) } - .flatMap { note.getNewNotes(it) } - .map { it.filter { note -> !note.isNotified } } - .doOnSuccess { - if (it.isNotEmpty()) { - Timber.d("Found ${it.size} unread notes") - NoteNotification(applicationContext).sendNotification(it) - } - } - .map { it.map { note -> note.apply { isNotified = true } } } - .flatMapCompletable { note.updateNotes(it) } - .subscribe({}, { Timber.e("Notifications sending failed") }) - ) - } - - override fun onDestroy() { - super.onDestroy() - disposable.clear() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/notification/BaseNotification.kt b/app/src/main/java/io/github/wulkanowy/services/notification/BaseNotification.kt deleted file mode 100644 index 945d0b15..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/notification/BaseNotification.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.services.notification - -import android.app.Notification -import android.app.NotificationManager -import android.content.Context -import android.content.Context.NOTIFICATION_SERVICE -import android.os.Build.VERSION.SDK_INT -import android.os.Build.VERSION_CODES.O -import androidx.core.app.NotificationCompat -import timber.log.Timber -import kotlin.random.Random - -abstract class BaseNotification(protected val context: Context) { - - protected val notificationManager: NotificationManager by lazy { - context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager - } - - fun notify(notification: Notification) { - notificationManager.notify(Random.nextInt(1000), notification) - } - - fun notificationBuilder(channelId: String): NotificationCompat.Builder { - if (SDK_INT >= O) createChannel(channelId) - return NotificationCompat.Builder(context, channelId) - } - - fun cancelAll() { - notificationManager.cancelAll() - Timber.d("Notifications canceled") - } - - abstract fun createChannel(channelId: String) -} diff --git a/app/src/main/java/io/github/wulkanowy/services/notification/GradeNotification.kt b/app/src/main/java/io/github/wulkanowy/services/notification/GradeNotification.kt deleted file mode 100644 index df57ec28..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/notification/GradeNotification.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.services.notification - -import android.annotation.TargetApi -import android.app.Notification.VISIBILITY_PUBLIC -import android.app.NotificationChannel -import android.app.NotificationManager.IMPORTANCE_HIGH -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.content.Context -import androidx.core.app.NotificationCompat -import androidx.core.content.ContextCompat -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX -import timber.log.Timber - -class GradeNotification(context: Context) : BaseNotification(context) { - - private val channelId = "Grade_Notify" - - @TargetApi(26) - override fun createChannel(channelId: String) { - notificationManager.createNotificationChannel(NotificationChannel( - channelId, context.getString(R.string.notify_grade_channel), IMPORTANCE_HIGH - ).apply { - enableLights(true) - enableVibration(true) - lockscreenVisibility = VISIBILITY_PUBLIC - }) - } - - fun sendNotification(items: List) { - notify(notificationBuilder(channelId) - .setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items, items.size, items.size)) - .setContentText(context.resources.getQuantityString(R.plurals.notify_grade_new_items, items.size, items.size)) - .setSmallIcon(R.drawable.ic_stat_notify_grade) - .setAutoCancel(true) - .setDefaults(NotificationCompat.DEFAULT_ALL) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setColor(ContextCompat.getColor(context, 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, items.size, items.size)) - items.forEach { - addLine("${it.subject}: ${it.entry}") - } - this - }) - .build() - ) - - Timber.d("Notification sent") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/notification/MessageNotification.kt b/app/src/main/java/io/github/wulkanowy/services/notification/MessageNotification.kt deleted file mode 100644 index 8733cada..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/notification/MessageNotification.kt +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.wulkanowy.services.notification - -import android.annotation.TargetApi -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.PendingIntent -import android.content.Context -import androidx.core.app.NotificationCompat -import androidx.core.content.ContextCompat -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.ui.modules.main.MainActivity -import timber.log.Timber - -class MessageNotification(context: Context) : BaseNotification(context) { - - private val channelId = "Message_Notify" - - @TargetApi(26) - override fun createChannel(channelId: String) { - notificationManager.createNotificationChannel(NotificationChannel( - channelId, context.getString(R.string.notify_message_channel), NotificationManager.IMPORTANCE_HIGH - ).apply { - enableLights(true) - enableVibration(true) - lockscreenVisibility = Notification.VISIBILITY_PUBLIC - }) - } - - fun sendNotification(items: List) { - notify(notificationBuilder(channelId) - .setContentTitle(context.resources.getQuantityString(R.plurals.message_new_items, items.size, items.size)) - .setContentText(context.resources.getQuantityString(R.plurals.notify_message_new_items, items.size, items.size)) - .setSmallIcon(R.drawable.ic_stat_notify_message) - .setAutoCancel(true) - .setDefaults(NotificationCompat.DEFAULT_ALL) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setColor(ContextCompat.getColor(context, R.color.colorPrimary)) - .setContentIntent( - PendingIntent.getActivity(context, 0, - MainActivity.getStartIntent(context).putExtra(MainActivity.EXTRA_START_MENU_INDEX, 4), - PendingIntent.FLAG_UPDATE_CURRENT - ) - ) - .setStyle(NotificationCompat.InboxStyle().run { - setSummaryText(context.resources.getQuantityString(R.plurals.message_number_item, items.size, items.size)) - items.forEach { - addLine("${it.sender}: ${it.subject}") - } - this - }) - .build() - ) - - Timber.d("Notification sent") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/notification/NoteNotification.kt b/app/src/main/java/io/github/wulkanowy/services/notification/NoteNotification.kt deleted file mode 100644 index 33c2fdc7..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/notification/NoteNotification.kt +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.wulkanowy.services.notification - -import android.annotation.TargetApi -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.PendingIntent -import android.content.Context -import androidx.core.app.NotificationCompat -import androidx.core.content.ContextCompat -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.ui.modules.main.MainActivity -import timber.log.Timber - -class NoteNotification(context: Context) : BaseNotification(context) { - - private val channelId = "Note_Notify" - - @TargetApi(26) - override fun createChannel(channelId: String) { - notificationManager.createNotificationChannel(NotificationChannel( - channelId, context.getString(R.string.notify_note_channel), NotificationManager.IMPORTANCE_HIGH - ).apply { - enableLights(true) - enableVibration(true) - lockscreenVisibility = Notification.VISIBILITY_PUBLIC - }) - } - - fun sendNotification(items: List) { - notify(notificationBuilder(channelId) - .setContentTitle(context.resources.getQuantityString(R.plurals.note_new_items, items.size, items.size)) - .setContentText(context.resources.getQuantityString(R.plurals.notify_note_new_items, items.size, items.size)) - .setSmallIcon(R.drawable.ic_stat_notify_note) - .setAutoCancel(true) - .setDefaults(NotificationCompat.DEFAULT_ALL) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setColor(ContextCompat.getColor(context, R.color.colorPrimary)) - .setContentIntent( - PendingIntent.getActivity(context, 0, - MainActivity.getStartIntent(context).putExtra(MainActivity.EXTRA_START_MENU_INDEX, 4), - PendingIntent.FLAG_UPDATE_CURRENT - ) - ) - .setStyle(NotificationCompat.InboxStyle().run { - setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, items.size, items.size)) - items.forEach { - addLine("${it.teacher}: ${it.category}") - } - this - }) - .build() - ) - - Timber.d("Notification sent") - } -} 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 d1e275b7..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt +++ /dev/null @@ -1,31 +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.SemesterRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.repositories.TimetableRepository -import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetFactory -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 - - override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory { - AndroidInjection.inject(this) - return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, sharedPref, 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 00000000..c5c66df0 --- /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 f227dc19..00000000 --- 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 00000000..2a4dc569 --- /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 00000000..efb9d61a --- /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 667b46ff..00000000 --- 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(fragmentManager, 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/BasePagerAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BasePagerAdapter.kt deleted file mode 100644 index 000efdcd..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BasePagerAdapter.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.ui.base - -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentStatePagerAdapter - -class BasePagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAdapter(fragmentManager) { - - val fragments = mutableMapOf() - - val registeredFragments = mutableMapOf() - - override fun getItem(position: Int) = fragments.values.elementAt(position) - - override fun getCount() = fragments.size - - override fun getPageTitle(position: Int): CharSequence? { - return fragments.keys.elementAtOrNull(position) - } - - override fun instantiateItem(container: ViewGroup, position: Int): Any { - return super.instantiateItem(container, position).also { - registeredFragments[position] = it as Fragment - } - } - - override fun destroyItem(container: ViewGroup, position: Int, fragment: Any) { - registeredFragments.remove(position) - super.destroyItem(container, position, fragment) - } -} 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 00000000..dfb60b6f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.java @@ -0,0 +1,35 @@ +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; + } + + 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 c5a31958..00000000 --- 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 d2470fd1..00000000 --- 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 db3918b9..00000000 --- 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 e0e32e2c..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.ui.base - -import android.content.res.Resources -import io.github.wulkanowy.R -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) { - - var showErrorMessage: (String, Throwable) -> Unit = { _, _ -> } - - fun dispatch(error: Throwable) { - 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) - 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 42622b50..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.ui.base.session - -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.main.MainActivity - -open class BaseSessionFragment : BaseFragment(), BaseSessionView { - - override fun showExpiredDialog() { - (activity as? MainActivity)?.showExpiredDialog() - } -} 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 17ed6537..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt +++ /dev/null @@ -1,12 +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.onDecryptionFail = { view.showExpiredDialog() } - } -} 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 81ce4d8f..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.wulkanowy.ui.base.session - -import io.github.wulkanowy.ui.base.BaseView - -interface BaseSessionView : BaseView { - - fun showExpiredDialog() -} 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 89654732..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.ui.base.session - -import android.content.res.Resources -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.security.ScramblerException -import javax.inject.Inject - -class SessionErrorHandler @Inject constructor(resources: Resources) : ErrorHandler(resources) { - - var onDecryptionFail: () -> Unit = {} - - override fun proceed(error: Throwable) { - when (error) { - is ScramblerException -> onDecryptionFail() - else -> super.proceed(error) - } - } - - override fun clear() { - super.clear() - onDecryptionFail = {} - } -} 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 00000000..95c5548b --- /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 00000000..fa074896 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginContract.java @@ -0,0 +1,53 @@ +package io.github.wulkanowy.ui.login; + +import io.github.wulkanowy.data.RepositoryContract; +import io.github.wulkanowy.di.annotations.PerActivity; +import io.github.wulkanowy.ui.base.BaseContract; + +public interface LoginContract { + interface View extends BaseContract.View { + + void setErrorEmailRequired(); + + void setErrorPassRequired(); + + void setErrorSymbolRequired(); + + void setErrorEmailInvalid(); + + void setErrorPassInvalid(); + + void setErrorPassIncorrect(); + + void resetViewErrors(); + + void setStepOneLoginProgress(); + + void setStepTwoLoginProgress(); + + void openMainActivity(); + + void showLoginProgress(boolean show); + + void showSoftInput(); + + void hideSoftInput(); + + } + + @PerActivity + interface Presenter extends BaseContract.Presenter { + + void attemptLogin(String email, String password, String symbol); + + void onStartAsync(); + + 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 00000000..c8e84725 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginPresenter.java @@ -0,0 +1,143 @@ +package io.github.wulkanowy.ui.login; + +import android.text.TextUtils; + +import java.util.LinkedHashMap; + +import javax.inject.Inject; + +import io.github.wulkanowy.api.login.AccountPermissionException; +import io.github.wulkanowy.api.login.BadCredentialsException; +import io.github.wulkanowy.data.RepositoryContract; +import io.github.wulkanowy.ui.base.BasePresenter; +import io.github.wulkanowy.utils.AppConstant; + +public class LoginPresenter extends BasePresenter + implements LoginContract.Presenter { + + private LoginTask loginAsync; + + @Inject + LoginPresenter(RepositoryContract repository) { + super(repository); + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (loginAsync != null) { + loginAsync.cancel(true); + loginAsync = null; + } + } + + @Override + public void attemptLogin(String email, String password, String symbol) { + getView().resetViewErrors(); + + if (!isAllFieldCorrect(password, email)) { + getView().showSoftInput(); + return; + } + + if (getView().isNetworkConnected()) { + // Dopóki używamy AsyncTask presenter będzie musiał "wiedzieć" o AsyncTaskach + loginAsync = new LoginTask(this, + email, + password, + getNormalizedSymbol(symbol)); + loginAsync.execute(); + + } else { + getView().onNoNetworkError(); + } + + getView().hideSoftInput(); + } + + @Override + public void onStartAsync() { + getView().showLoginProgress(true); + } + + @Override + public void onLoginProgress(int step) { + if (step == 1) { + getView().setStepOneLoginProgress(); + } else if (step == 2) { + getView().setStepTwoLoginProgress(); + } + } + + @Override + public void onEndAsync(boolean success, Exception exception) { + if (success) { + getView().openMainActivity(); + } else if (exception instanceof BadCredentialsException) { + getView().setErrorPassIncorrect(); + getView().showSoftInput(); + getView().showLoginProgress(false); + } else if (exception instanceof AccountPermissionException) { + getView().setErrorSymbolRequired(); + getView().showSoftInput(); + getView().showLoginProgress(false); + } else { + getView().onError(getRepository().getErrorLoginMessage(exception)); + getView().showLoginProgress(false); + } + + } + + @Override + public void onCanceledAsync() { + getView().showLoginProgress(false); + } + + private boolean isEmailValid(String email) { + return email.contains("@") || email.contains("\\\\"); + } + + private boolean isPasswordValid(String password) { + return password.length() > 4; + } + + private String getNormalizedSymbol(String symbol) { + if (TextUtils.isEmpty(symbol)) { + return AppConstant.DEFAULT_SYMBOL; + } + + String[] keys = getRepository().getSymbolsKeysArray(); + String[] values = getRepository().getSymbolsValuesArray(); + LinkedHashMap 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; + } +} 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 00000000..441b8d8d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginTask.java @@ -0,0 +1,58 @@ +package io.github.wulkanowy.ui.login; + +import android.os.AsyncTask; + +public class LoginTask extends AsyncTask { + + private String email; + + private String password; + + private String symbol; + + private LoginContract.Presenter presenter; + + private Exception exception; + + LoginTask(LoginContract.Presenter presenter, String email, String password, String symbol) { + this.presenter = presenter; + this.email = email; + this.password = password; + this.symbol = symbol; + } + + @Override + protected void onPreExecute() { + presenter.onStartAsync(); + } + + @Override + protected Boolean doInBackground(Void... params) { + try { + publishProgress(1); + presenter.getRepository().loginUser(email, password, symbol); + + publishProgress(2); + presenter.getRepository().syncAll(); + } 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 00000000..d4c0b9da --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java @@ -0,0 +1,153 @@ +package io.github.wulkanowy.ui.main; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.view.View; + +import com.aurelhubert.ahbottomnavigation.AHBottomNavigation; +import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem; +import com.aurelhubert.ahbottomnavigation.AHBottomNavigationViewPager; + +import javax.inject.Inject; + +import butterknife.BindView; +import butterknife.ButterKnife; +import io.github.wulkanowy.R; +import io.github.wulkanowy.services.SyncJob; +import io.github.wulkanowy.ui.base.BaseActivity; +import io.github.wulkanowy.ui.main.dashboard.DashboardFragment; +import io.github.wulkanowy.ui.main.grades.GradesFragment; +import io.github.wulkanowy.ui.main.timetable.TimetableFragment; + +public class MainActivity extends BaseActivity implements MainContract.View, + AHBottomNavigation.OnTabSelectedListener, OnFragmentIsReadyListener { + + private int initTabPosition = 0; + + @BindView(R.id.main_activity_nav) + AHBottomNavigation bottomNavigation; + + @BindView(R.id.main_activity_view_pager) + AHBottomNavigationViewPager viewPager; + + @BindView(R.id.main_activity_progress_bar) + View progressBar; + + @Inject + MainPagerAdapter pagerAdapter; + + @Inject + MainContract.Presenter presenter; + + public static Intent getStartIntent(Context context) { + return new Intent(context, MainActivity.class); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + initTabPosition = getIntent().getIntExtra(SyncJob.EXTRA_INTENT_KEY, initTabPosition); + + getActivityComponent().inject(this); + setButterKnife(ButterKnife.bind(this)); + + presenter.onStart(this); + + initiationViewPager(); + initiationBottomNav(); + } + + @Override + public void showProgressBar(boolean show) { + progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE); + viewPager.setVisibility(show ? View.INVISIBLE : View.VISIBLE); + bottomNavigation.setVisibility(show ? View.INVISIBLE : View.VISIBLE); + } + + @Override + public void showActionBar() { + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.show(); + } + } + + @Override + public void hideActionBar() { + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.hide(); + } + } + + @Override + public boolean onTabSelected(int position, boolean wasSelected) { + presenter.onTabSelected(position, wasSelected); + return true; + } + + @Override + public void setCurrentPage(int position) { + viewPager.setCurrentItem(position, false); + } + + @Override + public void onFragmentIsReady() { + presenter.onFragmentIsReady(); + } + + private void initiationBottomNav() { + bottomNavigation.addItem(new AHBottomNavigationItem( + getString(R.string.grades_text), + getResources().getDrawable(R.drawable.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 DashboardFragment()); + pagerAdapter.addFragment(new DashboardFragment()); + pagerAdapter.addFragment(new TimetableFragment()); + pagerAdapter.addFragment(new DashboardFragment()); + + viewPager.setPagingEnabled(false); + viewPager.setAdapter(pagerAdapter); + viewPager.setOffscreenPageLimit(4); + viewPager.setCurrentItem(initTabPosition, false); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + presenter.onDestroy(); + } +} 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 00000000..cb1d5d7c --- /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 00000000..fcc51fb5 --- /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 00000000..149e49e2 --- /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 00000000..21e3ba12 --- /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/attendance/AttendanceContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java new file mode 100644 index 00000000..fe403717 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java @@ -0,0 +1,23 @@ +package io.github.wulkanowy.ui.main.attendance; + +import io.github.wulkanowy.di.annotations.PerFragment; +import io.github.wulkanowy.ui.base.BaseContract; +import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; + +public interface AttendanceContract { + + interface View extends BaseContract.View { + + void setActivityTitle(); + + boolean isMenuVisible(); + } + + @PerFragment + interface Presenter extends BaseContract.Presenter { + + void onStart(View view, OnFragmentIsReadyListener listener); + + void onFragmentVisible(boolean isVisible); + } +} 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 00000000..6010d108 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java @@ -0,0 +1,59 @@ +package io.github.wulkanowy.ui.main.attendance; + +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 AttendanceFragment extends BaseFragment implements AttendanceContract.View { + + @Inject + AttendanceContract.Presenter presenter; + + public AttendanceFragment() { + // empty constructor for fragment + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, + 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 setActivityTitle() { + setTitle(getString(R.string.dashboard_text)); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + presenter.onDestroy(); + } +} 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 00000000..da43e320 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java @@ -0,0 +1,37 @@ +package io.github.wulkanowy.ui.main.attendance; + +import javax.inject.Inject; + +import io.github.wulkanowy.data.RepositoryContract; +import io.github.wulkanowy.ui.base.BasePresenter; +import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; + +public class AttendancePresenter extends BasePresenter + implements AttendanceContract.Presenter { + + private OnFragmentIsReadyListener listener; + + @Inject + AttendancePresenter(RepositoryContract repository) { + super(repository); + } + + @Override + public void onStart(AttendanceContract.View view, OnFragmentIsReadyListener listener) { + super.onStart(view); + this.listener = listener; + + if (getView().isMenuVisible()) { + getView().setActivityTitle(); + } + + this.listener.onFragmentIsReady(); + } + + @Override + public void onFragmentVisible(boolean isVisible) { + if (isVisible) { + getView().setActivityTitle(); + } + } +} 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 00000000..2488eb95 --- /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 00000000..0832b5fb --- /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 00000000..58d89b39 --- /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 00000000..5edabb7b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradeHeaderItem.java @@ -0,0 +1,117 @@ +package io.github.wulkanowy.ui.main.grades; + + +import android.content.res.Resources; +import android.view.View; +import android.widget.TextView; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; + +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem; +import eu.davidea.viewholders.ExpandableViewHolder; +import io.github.wulkanowy.R; +import io.github.wulkanowy.data.db.dao.entities.Subject; +import io.github.wulkanowy.utils.AverageCalculator; + +public class GradeHeaderItem + extends AbstractExpandableHeaderItem { + + 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(isSubItemsRead(subItems) ? View.INVISIBLE : View.VISIBLE); + alertImage.setTag(item.getName()); + } + + private boolean isSubItemsRead(List subItems) { + boolean isRead = true; + + for (GradesSubItem item : subItems) { + isRead = item.getGrade().getRead(); + } + return isRead; + } + + private String getGradesAverageString(Subject item) { + float average = AverageCalculator.calculate(item.getGradeList()); + + if (average < 0) { + return resources.getString(R.string.info_no_average); + } else { + return resources.getString(R.string.info_average_grades, average); + } + } + } +} 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 00000000..7deabb5e --- /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 00000000..0d9a699f --- /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 00000000..bd44f298 --- /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 00000000..cae2fbcb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesPresenter.java @@ -0,0 +1,157 @@ +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().loginCurrentUser(); + getRepository().syncSubjects(); + getRepository().syncGrades(); + } + + @Override + public void onCanceledRefreshAsync() { + 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() { + super.onDestroy(); + if (refreshTask != null) { + refreshTask.cancel(true); + refreshTask = null; + } + if (loadingTask != null) { + loadingTask.cancel(true); + loadingTask = null; + } + } +} 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 00000000..c252bb2a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSubItem.java @@ -0,0 +1,150 @@ +package io.github.wulkanowy.ui.main.grades; + +import android.app.Activity; +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; + + GradesSubItem(GradeHeaderItem header, Grade grade) { + super(header); + this.grade = grade; + } + + public Grade getGrade() { + return grade; + } + + @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); + } + + 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 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) { + this.item = item; + + 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) { + ((Activity) context).findViewById(R.id.grade_fragment_container) + .findViewWithTag(item.getSubject()).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/TabsData.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TabsData.java new file mode 100644 index 00000000..156d1759 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TabsData.java @@ -0,0 +1,37 @@ +package io.github.wulkanowy.ui.main.timetable; + +import android.support.v4.app.Fragment; + +import java.util.ArrayList; +import java.util.List; + +class TabsData { + + private List fragments = new ArrayList<>(); + + private List titles = new ArrayList<>(); + + Fragment getFragment(int index) { + return fragments.get(index); + } + + void addFragment(Fragment fragment) { + if (fragment != null) { + fragments.add(fragment); + } + } + + int getFragmentsCount() { + return fragments.size(); + } + + String getTitle(int index) { + return titles.get(index); + } + + void addTitle(String title) { + if (title != null) { + titles.add(title); + } + } +} 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 00000000..18fa33f9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java @@ -0,0 +1,35 @@ +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; + +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 00000000..cfc1e073 --- /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.Lesson; + +public class TimetableDialogFragment extends DialogFragment { + + private static final String ARGUMENT_KEY = "Item"; + + private Lesson 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(Lesson 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 = (Lesson) 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 00000000..a9a83391 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java @@ -0,0 +1,114 @@ +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; + +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 00000000..c418d9ac --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableHeaderItem.java @@ -0,0 +1,100 @@ +package io.github.wulkanowy.ui.main.timetable; + +import android.view.View; +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.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; + + 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); + } + + 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 00000000..24e7582d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePagerAdapter.java @@ -0,0 +1,36 @@ +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; + +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 00000000..bee323dd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java @@ -0,0 +1,107 @@ +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.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() { + super.onDestroy(); + if (loadingTask != null) { + loadingTask.cancel(true); + loadingTask = null; + } + } +} 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 00000000..b4212940 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableSubItem.java @@ -0,0 +1,123 @@ +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 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.Lesson; + + +public class TimetableSubItem + extends AbstractSectionableItem { + + private Lesson lesson; + + public TimetableSubItem(TimetableHeaderItem header, Lesson lesson) { + super(header); + this.lesson = lesson; + } + + public Lesson getLesson() { + return lesson; + } + + @Override + public boolean equals(Object o) { + return this == o; + } + + @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 Lesson item; + + SubItemViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + ButterKnife.bind(this, view); + context = view.getContext(); + view.setOnClickListener(this); + } + + void onBind(Lesson 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 00000000..fbffdb12 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabContract.java @@ -0,0 +1,30 @@ +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 showProgressBar(boolean show); + } + + 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 00000000..10f3163a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabFragment.java @@ -0,0 +1,145 @@ +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 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 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; + + @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; + } + + @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 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); + } + + 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 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 00000000..5872b4d0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableTabPresenter.java @@ -0,0 +1,155 @@ +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.Lesson; +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 boolean isFirstSight = false; + + @Inject + TimetableTabPresenter(RepositoryContract repository) { + super(repository); + } + + @Override + public void onStart(TimetableTabContract.View view, boolean isPrimary) { + super.onStart(view); + getView().showProgressBar(true); + 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() { + // do nothing + } + + @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) { + syncData(); + week = getRepository().getWeek(date); + } + + List dayList = week.getDayList(); + + headerItems = new ArrayList<>(); + + for (Day day : dayList) { + TimetableHeaderItem headerItem = new TimetableHeaderItem(day); + + List lessonList = day.getLessons(); + + List subItems = new ArrayList<>(); + + for (Lesson lesson : lessonList) { + subItems.add(new TimetableSubItem(headerItem, lesson)); + } + + headerItem.setSubItems(subItems); + headerItem.setExpanded(false); + headerItems.add(headerItem); + } + } + + @Override + public void onCanceledLoadingAsync() { + // do nothing + } + + @Override + public void onEndLoadingAsync(boolean result, Exception exception) { + getView().updateAdapterList(headerItems); + getView().showProgressBar(false); + } + + @Override + public void setArgumentDate(String date) { + this.date = date; + } + + private void syncData() throws Exception { + getRepository().loginCurrentUser(); + getRepository().syncTimetable(date); + } + + @Override + public void onDestroy() { + super.onDestroy(); + isFirstSight = false; + + if (refreshTask != null) { + refreshTask.cancel(true); + refreshTask = null; + } + if (loadingTask != null) { + loadingTask.cancel(true); + loadingTask = null; + } + } +} 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 ddc4e11f..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt +++ /dev/null @@ -1,69 +0,0 @@ -package io.github.wulkanowy.ui.modules.about - -import android.content.Intent -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.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_source_code)) - .withAboutSpecial2(getString(R.string.about_feedback)) - .withFields(R.string::class.java.fields) - .withCheckCachedDetection(false) - .withExcludedLibraries("fastadapter", "AndroidIconics", "gson", - "Jsoup", "Retrofit", "okio", "OkHttp") - .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 openSourceWebView() { - startActivity(Intent.parseUri("https://github.com/wulkanowy/wulkanowy", 0)) - } - - override fun openIssuesWebView() { - 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 cc5ba7cf..00000000 --- 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 9a512f70..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt +++ /dev/null @@ -1,37 +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) { - - fun onExtraSelect(type: Libs.SpecialButton?) { - view?.run { - when (type) { - SPECIAL1 -> { - Timber.i("Opening github page") - analytics.logEvent("open_page", mapOf("name" to "github")) - openSourceWebView() - } - SPECIAL2 -> { - Timber.i("Opening issues page") - analytics.logEvent("open_page", mapOf("name" to "issues")) - openIssuesWebView() - } - SPECIAL3 -> { - //empty for now - } - } - } - } -} 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 a3ae2ada..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.ui.modules.about - -import io.github.wulkanowy.ui.base.BaseView - -interface AboutView : BaseView { - - fun openSourceWebView() - - fun openIssuesWebView() -} 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 32458f27..00000000 --- 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 a564306d..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt +++ /dev/null @@ -1,51 +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 { - return student.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/account/AccountPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt deleted file mode 100644 index e1459fe7..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt +++ /dev/null @@ -1,71 +0,0 @@ -package io.github.wulkanowy.ui.modules.account - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.repositories.StudentRepository -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 javax.inject.Inject - -class AccountPresenter @Inject constructor( - private val errorHandler: ErrorHandler, - private val studentRepository: StudentRepository, - private val schedulers: SchedulersProvider -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: AccountView) { - super.onAttachView(view) - view.initView() - loadData() - } - - fun onAddSelected() { - view?.openLoginView() - } - - fun onRemoveSelected() { - view?.showConfirmDialog() - } - - fun onLogoutConfirm() { - 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()) openClearLoginView() - else recreateView() - } - }, { errorHandler.dispatch(it) })) - } - - fun onItemSelected(item: AbstractFlexibleItem<*>) { - if (item is AccountItem) { - if (item.student.isCurrent) { - view?.dismissView() - } else { - disposable.add(studentRepository.switchStudent(item.student) - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ view?.recreateView() }, { errorHandler.dispatch(it) })) - } - } - } - - private fun loadData() { - disposable.add(studentRepository.getSavedStudents(false) - .map { it.map { item -> AccountItem(item) } } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ view?.updateData(it) }, { 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 ca74998a..00000000 --- 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 611dd999..00000000 --- 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 3b60d8cb..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt +++ /dev/null @@ -1,149 +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.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 - } - 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 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) { - AttendanceDialog.newInstance(lesson).show(fragmentManager, lesson.toString()) - } - - 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 c2698ebb..00000000 --- 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 createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun getLayoutRes(): Int = R.layout.item_attendance - - 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 { - return attendance.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/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt deleted file mode 100644 index 9be87b44..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ /dev/null @@ -1,128 +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.AttendanceRepository -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.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 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) - view.initView() - loadData(ofEpochDay(date ?: now().previousOrSameSchoolDay.toEpochDay())) - reloadView() - } - - fun onPreviousDay() { - loadData(currentDate.previousSchoolDay) - reloadView() - } - - fun onNextDay() { - loadData(currentDate.nextSchoolDay) - reloadView() - } - - fun onSwipeRefresh() { - loadData(currentDate, true) - } - - fun onViewReselected() { - 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) view?.showAttendanceDialog(item.attendance) - } - - fun onSummarySwitchSelected(): Boolean { - view?.openSummaryView() - return true - } - - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - 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) - } - } - .subscribe({ - view?.apply { - updateData(it) - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - } - analytics.logEvent("load_attendance", mapOf("items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd"))) - }) { - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - } - ) - } - } - - private fun reloadView() { - view?.apply { - showProgress(true) - showContent(false) - showEmpty(false) - clearData() - showNextButton(!currentDate.plusDays(1).isHolidays) - showPreButton(!currentDate.minusDays(1).isHolidays) - updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.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 f839e89a..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt +++ /dev/null @@ -1,39 +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 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 1a3fc16f..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt +++ /dev/null @@ -1,120 +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.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 showContent(show: Boolean) { - attendanceSummaryRecycler.visibility = if (show) VISIBLE else GONE - } - - override fun showSubjects(show: Boolean) { - attendanceSummarySubjects.visibility = if (show) VISIBLE else VISIBLE - } - - 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 3102ce11..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt +++ /dev/null @@ -1,82 +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 5086e80c..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt +++ /dev/null @@ -1,124 +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.AttendanceSummaryRepository -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.repositories.SubjectRepostory -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 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: SubjectRepostory, - 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) - view.initView() - loadData(subjectId ?: -1) - loadSubjects() - } - - fun onSwipeRefresh() { - loadData(currentSubjectId, true) - } - - fun onSubjectSelected(name: String) { - view?.run { - showContent(false) - showProgress(true) - clearView() - } - loadData(subjects.singleOrNull { it.name == name }?.realId ?: -1) - } - - private fun loadData(subjectId: Int, forceRefresh: Boolean = false) { - 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) - } - } - .subscribe({ - view?.apply { - showEmpty(it.first.isEmpty()) - showContent(it.first.isNotEmpty()) - updateDataSet(it.first, it.second) - } - analytics.logEvent("load_attendance_summary", mapOf("items" to it.first.size, "force_refresh" to forceRefresh, "item_id" to subjectId)) - }) { - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - } - ) - } - } - - private fun loadSubjects() { - 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({ - view?.run { - view?.updateSubjects(it) - showSubjects(true) - } - }, { 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 c258f71d..00000000 --- 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 af62a2de..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt +++ /dev/null @@ -1,26 +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 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 ed5092c9..00000000 --- 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 d39688d4..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt +++ /dev/null @@ -1,121 +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.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.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 - } - 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 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) { - ExamDialog.newInstance(exam).show(fragmentManager, exam.toString()) - } - - 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 0a5b862c..00000000 --- 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 318685e9..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt +++ /dev/null @@ -1,51 +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 { - return exam.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/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt deleted file mode 100644 index 88a0eaa5..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt +++ /dev/null @@ -1,123 +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.ExamRepository -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.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 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) - 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() { - loadData(currentDate, true) - } - - fun onExamItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is ExamItem) view?.showExamDialog(item.exam) - } - - fun onViewReselected() { - now().nextOrSameSchoolDay.also { - if (currentDate != it) { - loadData(it) - reloadView() - } else if (view?.isViewEmpty == false) view?.resetView() - } - } - - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - 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) - } - } - .subscribe({ - view?.apply { - updateData(it) - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - } - analytics.logEvent("load_exam", mapOf("items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd"))) - }) { - 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() { - view?.apply { - showProgress(true) - 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 b6a926f3..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt +++ /dev/null @@ -1,33 +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 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/GradeFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt deleted file mode 100644 index a4d948a9..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt +++ /dev/null @@ -1,140 +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.BasePagerAdapter -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment -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: BasePagerAdapter - - 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) - } - - override fun initView() { - pagerAdapter.fragments.putAll(mapOf( - getString(R.string.all_details) to GradeDetailsFragment.newInstance(), - getString(R.string.grade_menu_summary) to GradeSummaryFragment.newInstance() - )) - gradeViewPager.run { - adapter = pagerAdapter - setOnSelectPageListener { presenter.onPageSelected(it) } - } - gradeTabLayout.setupWithViewPager(gradeViewPager) - } - - 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() { - gradeEmpty.visibility = VISIBLE - } - - 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) { - (childFragmentManager.fragments[index] as GradeView.GradeChildView).onParentLoadData(semesterId, forceRefresh) - } - - override fun notifyChildParentReselected(index: Int) { - (pagerAdapter.registeredFragments[index] as? GradeView.GradeChildView)?.onParentReselected() - } - - override fun notifyChildSemesterChange(index: Int) { - (pagerAdapter.registeredFragments[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 c47ddc57..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeModule.kt +++ /dev/null @@ -1,31 +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.BasePagerAdapter -import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment -import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment - -@Module -abstract class GradeModule { - - @Module - companion object { - - @JvmStatic - @PerFragment - @Provides - fun provideGradePagerAdapter(fragment: GradeFragment) = BasePagerAdapter(fragment.childFragmentManager) - } - - @PerChildFragment - @ContributesAndroidInjector - abstract fun bindGradeDetailsFragment(): GradeDetailsFragment - - @PerChildFragment - @ContributesAndroidInjector - abstract fun binGradeSummaryFragment(): GradeSummaryFragment -} 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 d2dd8031..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt +++ /dev/null @@ -1,107 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.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.reactivex.Completable -import java.util.concurrent.TimeUnit.MILLISECONDS -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) - disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread) - .subscribe { - selectedIndex = savedIndex ?: 0 - view.initView() - loadData() - }) - } - - fun onViewReselected() { - view?.run { notifyChildParentReselected(currentPageIndex) } - } - - fun onSemesterSwitch(): Boolean { - if (semesters.isNotEmpty()) view?.showSemesterDialog(selectedIndex - 1) - return true - } - - fun onSemesterSelected(index: Int) { - if (selectedIndex != index - 1) { - selectedIndex = index + 1 - loadedSemesterId.clear() - view?.let { - notifyChildrenSemesterChange() - loadChild(it.currentPageIndex) - } - analytics.logEvent("changed_semester", mapOf("number" to index + 1)) - } - } - - fun onChildViewRefresh() { - view?.let { loadChild(it.currentPageIndex, true) } - } - - fun onChildViewLoaded(semesterId: Int) { - view?.apply { - showContent(true) - showProgress(false) - loadedSemesterId[currentPageIndex] = semesterId - } - } - - fun onPageSelected(index: Int) { - loadChild(index) - } - - private fun loadData() { - 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) - .subscribe({ view?.run { loadChild(currentPageIndex) } }) { - errorHandler.dispatch(it) - view?.run { - showProgress(false) - showEmpty() - } - }) - } - - private fun loadChild(index: Int, forceRefresh: Boolean = false) { - semesters.first { it.semesterName == selectedIndex }.semesterId.also { - if (forceRefresh || loadedSemesterId[index] != it) { - view?.notifyChildLoadData(index, it, forceRefresh) - } - } - } - - private fun notifyChildrenSemesterChange() { - for (i in 0..1) 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 3f1a197c..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt +++ /dev/null @@ -1,33 +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() - - fun showSemesterDialog(selectedIndex: Int) - - 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 19b462b1..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt +++ /dev/null @@ -1,78 +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.toFormattedString -import io.github.wulkanowy.utils.valueColor -import kotlinx.android.synthetic.main.dialog_grade.* - - -class GradeDetailsDialog : DialogFragment() { - - private lateinit var grade: Grade - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(grade: Grade): GradeDetailsDialog { - return GradeDetailsDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, grade) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - grade = getSerializable(ARGUMENT_KEY) as Grade - } - } - - 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.valueColor) - } - - 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 a9ee9ce2..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt +++ /dev/null @@ -1,161 +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.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 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 - } - 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() { - gradeDetailsAdapter.smoothScrollToPosition(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 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) { - GradeDetailsDialog.newInstance(grade).show(fragmentManager, grade.toString()) - } - - 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 5dec00a9..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt +++ /dev/null @@ -1,87 +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 - - 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 - - return true - } - - override fun hashCode(): Int { - var result = subject.hashCode() - result = 31 * result + number.hashCode() - result = 31 * result + average.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/GradeDetailsItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt deleted file mode 100644 index 18c2656d..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt +++ /dev/null @@ -1,68 +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 weightString: String, private val valueColor: Int) - : 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(valueColor) - } - gradeItemDescription.text = if (grade.description.isNotBlank()) grade.description else grade.gradeSymbol - 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 (weightString != other.weightString) return false - if (valueColor != other.valueColor) return false - - return true - } - - override fun hashCode(): Int { - var result = grade.hashCode() - result = 31 * result + weightString.hashCode() - result = 31 * result + valueColor - 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 b80a7108..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ /dev/null @@ -1,160 +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.GradeRepository -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.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.valueColor -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) { - view?.apply { - showGradeDialog(item.grade) - 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 { - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it) } - .flatMap { gradeRepository.getNewGrades(it.first { item -> item.semesterId == currentSemesterId }) } - .map { it.map { grade -> grade.apply { isRead = true } } } - .flatMapCompletable { gradeRepository.updateGrades(it) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ loadData(currentSemesterId, false) }, { errorHandler.dispatch(it) })) - return true - } - - fun onSwipeRefresh() { - view?.notifyParentRefresh() - } - - fun onParentViewReselected() { - view?.run { - if (!isViewEmpty) { - if (preferencesRepository.isGradeExpandable) collapseAllItems() - scrollToStart() - } - } - } - - fun onParentViewChangeSemester() { - view?.run { - showProgress(true) - showRefresh(false) - showContent(false) - showEmpty(false) - clearView() - } - disposable.clear() - } - - private fun loadData(semesterId: Int, forceRefresh: Boolean) { - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it) } - .flatMap { gradeRepository.getGrades(it.first { item -> item.semesterId == semesterId }, forceRefresh) } - .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) - notifyParentDataLoaded(semesterId) - } - } - .subscribe({ - view?.run { - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - updateData(it) - } - analytics.logEvent("load_grade_details", mapOf("items" to it.size, "force_refresh" to forceRefresh)) - }) { - 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, - weightString = view?.weightString.orEmpty(), - valueColor = item.valueColor - ) - } - } - } - } - } - - private fun formatAverage(average: Double): String { - return view?.run { - if (average == 0.0) emptyAverageString - else averageString.format(average) - }.orEmpty() - } - - private fun updateGrade(grade: Grade) { - disposable.add(gradeRepository.updateGrade(grade) - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({}) { error -> errorHandler.dispatch(error) }) - Timber.d("Grade ${grade.id} updated") - } -} 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 24254260..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt +++ /dev/null @@ -1,48 +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 - - fun initView() - - fun updateData(data: List) - - fun updateItem(item: AbstractFlexibleItem<*>) - - fun clearView() - - fun scrollToStart() - - fun collapseAllItems() - - fun showGradeDialog(grade: Grade) - - fun showContent(show: Boolean) - - fun showEmpty(show: Boolean) - - fun showProgress(show: 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/summary/GradeSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt deleted file mode 100644 index 13833b74..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt +++ /dev/null @@ -1,117 +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() { - gradeSummaryAdapter.smoothScrollToPosition(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 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/GradeSummaryHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryHeader.kt deleted file mode 100644 index fba3fde6..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryHeader.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.summary - -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.FlexibleViewHolder -import io.github.wulkanowy.R -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.header_grade_summary.* - -class GradeSummaryHeader(private val name: String, private val average: String) : AbstractHeaderItem() { - - override fun getLayoutRes() = R.layout.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?.run { - gradeSummaryHeaderName.text = name - gradeSummaryHeaderAverage.text = average - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as GradeSummaryHeader - - if (name != other.name) return false - if (average != other.average) return false - - return true - } - - override fun hashCode(): Int { - var result = name.hashCode() - result = 31 * result + average.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/GradeSummaryItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt deleted file mode 100644 index 54302fa6..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.summary - -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.GradeSummary -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_grade_summary.* - -class GradeSummaryItem(header: GradeSummaryHeader, private val grade: String, private val title: String) : - AbstractSectionableItem(header) { - - 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 { - gradeSummaryItemGrade.text = grade - gradeSummaryItemTitle.text = title - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as GradeSummaryItem - - if (grade != other.grade) return false - if (title != other.title) return false - if (header != other.header) return false - - return true - } - - override fun hashCode(): Int { - var result = header.hashCode() - result = 31 * result + grade.hashCode() - result = 31 * result + title.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 a67ad894..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ /dev/null @@ -1,131 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.summary - -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.repositories.GradeRepository -import io.github.wulkanowy.data.repositories.GradeSummaryRepository -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.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 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) { - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it) } - .map { semester -> semester.first { it.semesterId == semesterId } } - .flatMap { - gradeSummaryRepository.getGradesSummary(it, forceRefresh) - .flatMap { gradesSummary -> - gradeRepository.getGrades(it, 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) - notifyParentDataLoaded(semesterId) - } - }.subscribe({ - view?.run { - showEmpty(it.first.isEmpty()) - showContent(it.first.isNotEmpty()) - updateData(it.first, it.second) - } - analytics.logEvent("load_grade_summary", mapOf("items" to it.first.size, "force_refresh" to forceRefresh)) - }) { - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } - - fun onSwipeRefresh() { - view?.notifyParentRefresh() - } - - fun onParentViewReselected() { - view?.run { - if (!isViewEmpty) resetView() - } - } - - fun onParentViewChangeSemester() { - view?.run { - showProgress(true) - showRefresh(false) - showContent(false) - showEmpty(false) - clearView() - } - disposable.clear() - } - - private fun createGradeSummaryItems(gradesSummary: List, averages: Map) - : List { - return gradesSummary.filter { !checkEmpty(it, averages) } - .flatMap { gradeSummary -> - GradeSummaryHeader( - name = gradeSummary.subject, - average = formatAverage(averages.getOrElse(gradeSummary.subject) { 0.0 }, "") - ).let { - listOf(GradeSummaryItem( - header = it, - title = view?.predictedString.orEmpty(), - grade = gradeSummary.predictedGrade - ), GradeSummaryItem( - header = it, - title = view?.finalString.orEmpty(), - grade = gradeSummary.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 f1c535c7..00000000 --- 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 2602ec99..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt +++ /dev/null @@ -1,32 +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 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 42784188..00000000 --- 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 135098fb..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt +++ /dev/null @@ -1,110 +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.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.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 - } - 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 updateNavigationDay(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 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) { - HomeworkDialog.newInstance(homework).show(fragmentManager, homework.toString()) - } - - 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/HomeworkItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt deleted file mode 100644 index 97ff188c..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import android.annotation.SuppressLint -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.Homework -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_homework.* - -class HomeworkItem(val homework: Homework) : AbstractFlexibleItem() { - - override fun getLayoutRes(): Int = R.layout.item_homework - - 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 { - 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 { - return homework.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/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt deleted file mode 100644 index e4d42745..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt +++ /dev/null @@ -1,100 +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.repositories.HomeworkRepository -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.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.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 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) - view.initView() - loadData(LocalDate.ofEpochDay(date ?: LocalDate.now().nextOrSameSchoolDay.toEpochDay())) - reloadView() - } - - fun onPreviousDay() { - loadData(currentDate.previousSchoolDay) - reloadView() - } - - fun onNextDay() { - loadData(currentDate.nextSchoolDay) - reloadView() - } - - fun onSwipeRefresh() { - loadData(currentDate, true) - } - - fun onHomeworkItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is HomeworkItem) view?.showTimetableDialog(item.homework) - } - - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - currentDate = date - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .delay(200, TimeUnit.MILLISECONDS) - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { homeworkRepository.getHomework(it, currentDate, forceRefresh) } - .map { items -> items.map { HomeworkItem(it) } } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - hideRefresh() - showProgress(false) - } - } - .subscribe({ - view?.apply { - updateData(it) - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - } - analytics.logEvent("load_homework", mapOf("items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd"))) - }) { - view?.run { showEmpty(isViewEmpty()) } - errorHandler.dispatch(it) - }) - } - } - - private fun reloadView() { - view?.apply { - showProgress(true) - showContent(false) - showEmpty(false) - clearData() - showNextButton(!currentDate.plusDays(1).isHolidays) - showPreButton(!currentDate.minusDays(1).isHolidays) - updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize()) - } - } -} 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 521020e6..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt +++ /dev/null @@ -1,31 +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 updateNavigationDay(date: String) - - fun isViewEmpty(): Boolean - - fun hideRefresh() - - fun showEmpty(show: Boolean) - - fun showProgress(show: 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 c9c1aeb4..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt +++ /dev/null @@ -1,74 +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.ui.base.BaseActivity -import io.github.wulkanowy.ui.base.BasePagerAdapter -import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment -import io.github.wulkanowy.ui.modules.login.options.LoginOptionsFragment -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: BasePagerAdapter - - 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.fragments.putAll(mapOf( - "1" to LoginFormFragment.newInstance(), - "2" to LoginOptionsFragment.newInstance() - )) - - loginViewpager.run { - adapter = loginAdapter - setOnSelectPageListener { presenter.onPageSelected(it) } - } - } - - override fun switchView(index: Int) { - loginViewpager.setCurrentItem(index, false) - } - - override fun notifyOptionsViewLoadData() { - (supportFragmentManager.fragments[1] as? LoginOptionsFragment)?.onParentLoadData() - } - - fun onChildFragmentSwitchOptions() { - presenter.onChildViewSwitchOptions() - } - - override fun hideActionBar() { - supportActionBar?.hide() - } - - override fun onBackPressed() { - presenter.onBackPressed { super.onBackPressed() } - } - - 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 40f1d711..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import android.content.res.Resources -import android.database.sqlite.SQLiteConstraintException -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) : ErrorHandler(resources) { - - 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 3cdf8367..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt +++ /dev/null @@ -1,31 +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.BasePagerAdapter -import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment -import io.github.wulkanowy.ui.modules.login.options.LoginOptionsFragment - -@Module -internal abstract class LoginModule { - - @Module - companion object { - - @JvmStatic - @PerActivity - @Provides - fun provideLoginAdapter(activity: LoginActivity) = BasePagerAdapter(activity.supportFragmentManager) - } - - @PerFragment - @ContributesAndroidInjector() - abstract fun bindLoginFormFragment(): LoginFormFragment - - @PerFragment - @ContributesAndroidInjector() - abstract fun bindLoginOptionsFragment(): LoginOptionsFragment -} 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 0687e2f2..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import javax.inject.Inject - -class LoginPresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresenter(errorHandler) { - - override fun onAttachView(view: LoginView) { - super.onAttachView(view) - view.run { - initAdapter() - hideActionBar() - } - } - - fun onPageSelected(index: Int) { - if (index == 1) view?.notifyOptionsViewLoadData() - } - - fun onChildViewSwitchOptions() { - view?.switchView(1) - } - - fun onBackPressed(default: () -> Unit) { - view?.run { - if (currentViewIndex == 1) { - switchView(0) - hideActionBar() - } 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 52cb9180..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import io.github.wulkanowy.ui.base.BaseView - -interface LoginView : BaseView { - - val currentViewIndex: Int - - fun initAdapter() - - fun hideActionBar() - - fun switchView(index: Int) - - fun notifyOptionsViewLoadData() -} 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 7985ef6e..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt +++ /dev/null @@ -1,160 +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.DEBUG -import io.github.wulkanowy.BuildConfig.VERSION_NAME -import io.github.wulkanowy.R -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.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 isDebug = DEBUG - - 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() { - loginSignButton.setOnClickListener { - presenter.attemptLogin( - loginNicknameEdit.text.toString(), - loginPassEdit.text.toString(), - loginSymbolEdit.text.toString(), - resources.getStringArray(R.array.endpoints_values)[loginHostEdit.selectedItemPosition] - ) - } - - loginPassEdit.setOnEditorActionListener { _, id, _ -> onEditAction(id) } - - loginHostEdit.apply { - adapter = ArrayAdapter.createFromResource(context, R.array.endpoints_keys, android.R.layout.simple_spinner_item) - .apply { setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) } - } - - loginSymbolEdit.run { - setAdapter(ArrayAdapter(context, android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values))) - setOnEditorActionListener { _, id, _ -> onEditAction(id) } - } - } - - override fun showSymbolInput() { - loginHeader.text = getString(R.string.login_header_symbol) - loginMainForm.visibility = GONE - loginSymbolInput.visibility = VISIBLE - loginSymbolEdit.requestFocus() - showSoftKeyboard() - } - - @SuppressLint("SetTextI18n") - override fun showVersion() { - loginVersion.apply { - visibility = VISIBLE - text = "${getString(R.string.app_name)} $VERSION_NAME" - } - } - - override fun switchOptionsView() { - (activity as? LoginActivity)?.onChildFragmentSwitchOptions() - } - - override fun setErrorNicknameRequired() { - loginNicknameEdit.run { - requestFocus() - error = getString(R.string.login_field_required) - } - } - - override fun setErrorPassRequired(focus: Boolean) { - loginPassEdit.run { - if (focus) requestFocus() - error = getString(R.string.login_field_required) - } - } - - override fun setErrorPassInvalid(focus: Boolean) { - loginPassEdit.run { - if (focus) requestFocus() - error = getString(R.string.login_invalid_password) - } - } - - override fun setErrorSymbolRequire() { - loginSymbolEdit.run { - requestFocus() - error = getString(R.string.login_field_required) - } - } - - override fun setErrorPassIncorrect() { - loginPassEdit.run { - requestFocus() - error = getString(R.string.login_incorrect_password) - } - } - - override fun setErrorSymbolIncorrect() { - loginSymbolEdit.run { - requestFocus() - error = getString(R.string.login_incorrect_symbol) - } - } - - override fun resetViewErrors() { - loginNicknameEdit.error = null - loginPassEdit.error = null - } - - override fun showSoftKeyboard() { - activity?.showSoftInput() - } - - override fun hideSoftKeyboard() { - activity?.hideSoftInput() - } - - override fun showProgress(show: Boolean) { - loginFormProgressContainer.visibility = if (show) VISIBLE else GONE - } - - override fun showContent(show: Boolean) { - loginFormContainer.visibility = if (show) VISIBLE else GONE - } - - private fun onEditAction(actionId: Int): Boolean { - return when (actionId) { - IME_ACTION_DONE, IME_NULL -> loginSignButton.callOnClick() - else -> false - } - } - - 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 58ce34a7..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt +++ /dev/null @@ -1,98 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.form - -import com.google.firebase.analytics.FirebaseAnalytics.Event.SIGN_UP -import com.google.firebase.analytics.FirebaseAnalytics.Param.GROUP_ID -import com.google.firebase.analytics.FirebaseAnalytics.Param.SUCCESS -import io.github.wulkanowy.data.repositories.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 - -class LoginFormPresenter @Inject constructor( - private val schedulers: SchedulersProvider, - private val errorHandler: LoginErrorHandler, - private val studentRepository: StudentRepository, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - private var wasEmpty = false - - 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 attemptLogin(email: String, password: String, symbol: String, endpoint: String) { - if (!validateCredentials(email, password, symbol)) return - - disposable.add(studentRepository.getStudents(email, password, symbol, endpoint) - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doOnSubscribe { - view?.apply { - hideSoftKeyboard() - showProgress(true) - showContent(false) - } - } - .doFinally { - view?.apply { - showProgress(false) - showContent(true) - } - } - .subscribe({ - view?.run { - if (it.isEmpty() && !wasEmpty) { - showSymbolInput() - wasEmpty = true - } else if (it.isEmpty() && wasEmpty) { - showSymbolInput() - setErrorSymbolIncorrect() - analytics.logEvent(SIGN_UP, mapOf(SUCCESS to false, "students" to it.size, "endpoint" to endpoint, GROUP_ID to symbol.ifEmpty { "nil" })) - } else { - switchOptionsView() - } - } - }, { - analytics.logEvent(SIGN_UP, mapOf(SUCCESS to true, "endpoint" to endpoint, GROUP_ID to symbol.ifEmpty { "nil" })) - errorHandler.dispatch(it) - })) - } - - private fun validateCredentials(login: String, password: String, symbol: String): Boolean { - var isCorrect = true - - if (login.isEmpty()) { - view?.setErrorNicknameRequired() - isCorrect = false - } - - if (password.isEmpty()) { - view?.setErrorPassRequired(focus = isCorrect) - isCorrect = false - } - - if (symbol.isEmpty() && wasEmpty) { - view?.setErrorSymbolRequire() - 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 4d540226..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.form - -import io.github.wulkanowy.ui.base.BaseView - -interface LoginFormView : BaseView { - - val isDebug: Boolean - - fun initView() - - fun switchOptionsView() - - fun setErrorNicknameRequired() - - fun setErrorPassRequired(focus: Boolean) - - fun setErrorSymbolRequire() - - fun setErrorPassInvalid(focus: Boolean) - - fun setErrorPassIncorrect() - - fun setErrorSymbolIncorrect() - - fun resetViewErrors() - - fun showVersion() - - fun showSoftKeyboard() - - fun hideSoftKeyboard() - - fun showProgress(show: Boolean) - - fun showContent(show: Boolean) - - fun showSymbolInput() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/options/LoginOptionsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/options/LoginOptionsFragment.kt deleted file mode 100644 index 3e306b19..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/options/LoginOptionsFragment.kt +++ /dev/null @@ -1,83 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.options - -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.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_login_options.* -import javax.inject.Inject - -class LoginOptionsFragment : BaseFragment(), LoginOptionsView { - - @Inject - lateinit var presenter: LoginOptionsPresenter - - @Inject - lateinit var loginAdapter: FlexibleAdapter> - - companion object { - fun newInstance() = LoginOptionsFragment() - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_login_options, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this) - } - - override fun initView() { - loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } } - - loginOptionsRecycler.apply { - adapter = loginAdapter - layoutManager = SmoothScrollLinearLayoutManager(context) - } - } - - fun onParentLoadData() { - presenter.onParentViewLoadData() - } - - 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) { - loginOptionsProgressContainer.visibility = if (show) VISIBLE else GONE - } - - override fun showContent(show: Boolean) { - loginOptionsRecycler.visibility = if (show) VISIBLE else GONE - } - - override fun showActionBar(show: Boolean) { - (activity as? AppCompatActivity)?.supportActionBar?.run { if (show) show() else hide() } - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/options/LoginOptionsItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/options/LoginOptionsItem.kt deleted file mode 100644 index 2d4de475..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/options/LoginOptionsItem.kt +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.options - -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_options.view.* - -class LoginOptionsItem(val student: Student) : AbstractFlexibleItem() { - - override fun getLayoutRes(): Int = R.layout.item_login_options - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ItemViewHolder { - return ItemViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ItemViewHolder?, - position: Int, payloads: MutableList?) { - holder?.bind(student) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as LoginOptionsItem - - 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 - - fun bind(item: Student) { - itemView.run { - loginItemName.text = item.studentName - loginItemSchool.text = item.schoolName - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/options/LoginOptionsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/options/LoginOptionsPresenter.kt deleted file mode 100644 index af9ae692..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/options/LoginOptionsPresenter.kt +++ /dev/null @@ -1,74 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.options - -import com.google.firebase.analytics.FirebaseAnalytics.Event.SIGN_UP -import com.google.firebase.analytics.FirebaseAnalytics.Param.GROUP_ID -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.SemesterRepository -import io.github.wulkanowy.data.repositories.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 javax.inject.Inject - -class LoginOptionsPresenter @Inject constructor( - private val errorHandler: LoginErrorHandler, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val schedulers: SchedulersProvider, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: LoginOptionsView) { - super.onAttachView(view) - view.run { - initView() - errorHandler.onStudentDuplicate = { showMessage(it) } - } - } - - fun onParentViewLoadData() { - disposable.add(studentRepository.cachedStudents - .observeOn(schedulers.mainThread) - .subscribeOn(schedulers.backgroundThread) - .doOnSubscribe { view?.showActionBar(true) } - .subscribe({ view?.updateData(it.map { student -> LoginOptionsItem(student) }) }, { errorHandler.dispatch(it) })) - } - - fun onItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is LoginOptionsItem) { - registerStudent(item.student) - } - } - - private fun registerStudent(student: Student) { - disposable.add(studentRepository.saveStudent(student) - .map { student.apply { id = it } } - .flatMap { semesterRepository.getSemesters(student, true) } - .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) - } - } - .subscribe({ - analytics.logEvent(SIGN_UP, mapOf(SUCCESS to true, "students" to 1, "endpoint" to student.endpoint, GROUP_ID to student.symbol)) - view?.openMainView() - }, { - errorHandler.dispatch(it) - view?.apply { - showProgress(false) - showContent(true) - showActionBar(true) - } - })) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/options/LoginOptionsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/options/LoginOptionsView.kt deleted file mode 100644 index e20912ae..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/options/LoginOptionsView.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.options - -import io.github.wulkanowy.ui.base.BaseView - -interface LoginOptionsView : 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/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt deleted file mode 100644 index 58c2f7c9..00000000 --- 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.os.Bundle -import android.view.Menu -import android.view.MenuItem -import androidx.appcompat.app.AlertDialog -import androidx.core.content.ContextCompat -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.services.notification.GradeNotification -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 onStart() { - super.onStart() - presenter.onViewStart() - } - - 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.onViewStart() } - 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() { - (navController.currentStack?.get(0) as? MainView.MainChildView)?.onFragmentReselected() - } - - fun pushView(fragment: Fragment) { - navController.pushFragment(fragment) - } - - override fun popView() { - navController.safelyPopFragment() - } - - override fun onBackPressed() { - presenter.onBackPressed { super.onBackPressed() } - } - - override fun cancelNotifications() { - GradeNotification(applicationContext).cancelAll() - } - - override fun openLoginView() { - startActivity(LoginActivity.getStartIntent(this) - .apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.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 9d1c44c0..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt +++ /dev/null @@ -1,92 +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.PerActivity -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.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 - -@Module -abstract class MainModule { - - @Module - companion object { - - @JvmStatic - @PerActivity - @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 bindsAccountDialog(): 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 f18bd9eb..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt +++ /dev/null @@ -1,92 +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.PreferencesRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.services.job.ServiceHelper -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 javax.inject.Inject - -class MainPresenter @Inject constructor( - private val errorHandler: ErrorHandler, - private val studentRepository: StudentRepository, - private val prefRepository: PreferencesRepository, - private val schedulers: SchedulersProvider, - private val serviceHelper: ServiceHelper, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - fun onAttachView(view: MainView, initMenuIndex: Int) { - super.onAttachView(view) - view.run { - cancelNotifications() - startMenuIndex = if (initMenuIndex != -1) initMenuIndex else prefRepository.startMenuIndex - initView() - } - serviceHelper.startFullSyncService() - - analytics.logEvent(APP_OPEN, mapOf(DESTINATION to when (initMenuIndex) { - 1 -> "Grades" - 3 -> "Timetable" - 4 -> "More" - else -> "User action" - })) - } - - fun onViewStart() { - view?.apply { - currentViewTitle?.let { setViewTitle(it) } - currentStackSize?.let { - if (it > 1) showHomeArrow(true) - else showHomeArrow(false) - } - } - } - - fun onAccountManagerSelected(): Boolean { - view?.showAccountPicker() - return true - } - - fun onUpNavigate(): Boolean { - view?.popView() - return true - } - - fun onBackPressed(default: () -> Unit) { - view?.run { - if (isRootView) default() - else popView() - } - } - - fun onTabSelected(index: Int, wasSelected: Boolean): Boolean { - return view?.run { - if (wasSelected) { - notifyMenuViewReselected() - false - } else { - switchMenuView(index) - true - } - } == true - } - - fun onLoginSelected() { - disposable.add(studentRepository.getCurrentStudent(false) - .flatMapCompletable { studentRepository.logoutStudent(it) } - .andThen(studentRepository.getSavedStudents(false)) - .flatMapCompletable { - if (it.isNotEmpty()) studentRepository.switchStudent(it[0]) - else Completable.complete() - } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ view?.openLoginView() }, { 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 112f3f4e..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt +++ /dev/null @@ -1,42 +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 cancelNotifications() - - 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 f75c6131..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt +++ /dev/null @@ -1,83 +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.MessagesRepository.MessageFolder.RECEIVED -import io.github.wulkanowy.data.repositories.MessagesRepository.MessageFolder.SENT -import io.github.wulkanowy.data.repositories.MessagesRepository.MessageFolder.TRASHED -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.base.BasePagerAdapter -import io.github.wulkanowy.ui.modules.main.MainView -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: BasePagerAdapter - - 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.fragments.putAll(mapOf( - getString(R.string.message_inbox) to MessageTabFragment.newInstance(RECEIVED), - getString(R.string.message_sent) to MessageTabFragment.newInstance(SENT), - getString(R.string.message_trash) to MessageTabFragment.newInstance(TRASHED) - )) - messageViewPager.run { - adapter = pagerAdapter - offscreenPageLimit = 2 - setOnSelectPageListener { presenter.onPageSelected(it) } - } - messageTabLayout.setupWithViewPager(messageViewPager) - } - - 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) { - (childFragmentManager.fragments[index] as MessageView.MessageChildView).onParentLoadData(forceRefresh) - } - - 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 e9aa242d..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt +++ /dev/null @@ -1,67 +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(): Int = R.layout.item_message - - override fun bindViewHolder( - adapter: FlexibleAdapter>, holder: ViewHolder, - position: Int, payloads: MutableList? - ) { - holder.apply { - val style = if (message.unread == true) BOLD else NORMAL - - messageItemAuthor.run { - text = if (message.recipient?.isNotBlank() == true) 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 dd7a22ae..00000000 --- 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.BasePagerAdapter -import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment - -@Module -abstract class MessageModule { - - @Module - companion object { - - @JvmStatic - @PerFragment - @Provides - fun provideGradePagerAdapter(fragment: MessageFragment) = BasePagerAdapter(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 360b9bfc..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt +++ /dev/null @@ -1,42 +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 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) - 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) { - view?.notifyChildLoadData(index, forceRefresh) - } - - fun onChildViewLoaded() { - view?.apply { - showContent(true) - showProgress(false) - } - } -} 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 b0bb8aa7..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt +++ /dev/null @@ -1,21 +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) - - 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 f104d87a..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ /dev/null @@ -1,81 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.preview - -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.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainView -import kotlinx.android.synthetic.main.fragment_message_preview.* -import javax.inject.Inject - -class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainView.TitledView { - - @Inject - lateinit var presenter: MessagePreviewPresenter - - 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 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 = message - presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getInt(MESSAGE_ID_KEY) ?: 0) - } - - override fun setSubject(subject: String) { - messageSubject.text = subject - } - - override fun setRecipient(recipient: String?) { - messageAuthor.text = getString(R.string.message_to, recipient) - } - - override fun setSender(sender: String?) { - messageAuthor.text = getString(R.string.message_from, sender) - } - - override fun setDate(date: String?) { - messageDate.text = getString(R.string.message_date, date) - } - - override fun setContent(content: String?) { - messageContent.text = content - } - - override fun showProgress(show: Boolean) { - messageProgress.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showMessageError() { - messageError.visibility = View.VISIBLE - } - - 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 3197e991..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.preview - -import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE -import io.github.wulkanowy.data.repositories.MessagesRepository -import io.github.wulkanowy.data.repositories.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 javax.inject.Inject - -class MessagePreviewPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val messagesRepository: MessagesRepository, - private val studentRepository: StudentRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - var messageId: Int = 0 - - fun onAttachView(view: MessagePreviewView, id: Int) { - super.onAttachView(view) - loadData(id) - } - - private fun loadData(id: Int) { - messageId = id - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .flatMap { messagesRepository.getMessage(it.studentId, messageId, true) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { view?.showProgress(false) } - .subscribe({ 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) - - if (it.recipient?.isNotBlank() == true) setRecipient(it.recipient) - else setSender(it.sender) - } - } - analytics.logEvent("load_attendance", mapOf(START_DATE to message.date?.toFormattedString("yyyy.MM.dd"), "lenght" to message.content?.length)) - }) { - view?.showMessageError() - errorHandler.dispatch(it) - }) - } - } -} 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 d8dd8965..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.preview - -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 showMessageError() -} 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 d3e2f471..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt +++ /dev/null @@ -1,122 +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.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.MessagesRepository -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: MessagesRepository.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, MessagesRepository.MessageFolder.valueOf( - (savedInstanceState ?: arguments)?.getString(MessageTabFragment.MESSAGE_TAB_FOLDER_ID) ?: "" - )) - } - - override fun initView() { - tabAdapter.setOnItemClickListener { presenter.onMessageItemSelected(it) } - - messageTabRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = tabAdapter - } - 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 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 0e1a9d84..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ /dev/null @@ -1,86 +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.MessagesRepository -import io.github.wulkanowy.data.repositories.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 messagesRepository: MessagesRepository, - private val studentRepository: StudentRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - lateinit var folder: MessagesRepository.MessageFolder - - fun onAttachView(view: MessageTabView, folder: MessagesRepository.MessageFolder) { - super.onAttachView(view) - view.initView() - this.folder = folder - } - - fun onSwipeRefresh() { - onParentViewLoadData(true) - } - - fun onParentViewLoadData(forceRefresh: Boolean) { - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .flatMap { messagesRepository.getMessages(it.studentId, folder, forceRefresh) } - .map { items -> items.map { MessageItem(it, view?.noSubjectString.orEmpty()) } } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - showRefresh(false) - showProgress(false) - notifyParentDataLoaded() - } - } - .subscribe({ - view?.run { - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - updateData(it) - } - analytics.logEvent("load_messages", mapOf("items" to it.size, "folder" to folder.name)) - }) { - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } - } - - fun onMessageItemSelected(item: AbstractFlexibleItem<*>) { - if (item is MessageItem) { - view?.run { - openMessage(item.message.realId) - if (item.message.unread == true) { - item.message.unread = false - updateItem(item) - updateMessage(item.message) - } - } - } - } - - private fun updateMessage(message: Message) { - disposable.add(messagesRepository.updateMessage(message) - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ - Timber.d("Message ${message.realId} updated") - }) { error -> 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 309e2766..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt +++ /dev/null @@ -1,32 +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 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 226fccd6..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt +++ /dev/null @@ -1,133 +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.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 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 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 e8571a8c..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt +++ /dev/null @@ -1,48 +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 61b187f1..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt +++ /dev/null @@ -1,45 +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 javax.inject.Inject - -class MorePresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresenter(errorHandler) { - - override fun onAttachView(view: MoreView) { - super.onAttachView(view) - view.initView() - loadData() - } - - fun onItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is MoreItem) { - view?.run { - when (item.title) { - messagesRes?.first -> openMessagesView() - homeworkRes?.first -> openHomeworkView() - noteRes?.first -> openNoteView() - settingsRes?.first -> openSettingsView() - aboutRes?.first -> openAboutView() - } - } - } - } - - fun onViewReselected() { - view?.popView() - } - - private fun loadData() { - 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) }, - 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 08ff82ad..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt +++ /dev/null @@ -1,33 +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 settingsRes: Pair? - - val aboutRes: Pair? - - fun initView() - - fun updateData(data: List) - - fun openSettingsView() - - fun openAboutView() - - fun popView() - - fun openMessagesView() - - fun openHomeworkView() - - fun openNoteView() -} 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 492aeab2..00000000 --- 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 243f2575..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt +++ /dev/null @@ -1,95 +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.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.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 - } - noteSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun showNoteDialog(note: Note) { - NoteDialog.newInstance(note).show(fragmentManager, note.toString()) - } - - 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 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 97635a2c..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt +++ /dev/null @@ -1,61 +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 createViewHolder(view: View, adapter: FlexibleAdapter>): NoteItem.ViewHolder { - return NoteItem.ViewHolder(view, adapter) - } - - override fun getLayoutRes(): Int = R.layout.item_note - - 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 - return true - } - - override fun hashCode(): Int { - return note.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/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt deleted file mode 100644 index c388a3f7..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt +++ /dev/null @@ -1,83 +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.NoteRepository -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.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) - view.initView() - loadData() - } - - fun onSwipeRefresh() { - loadData(true) - } - - private fun loadData(forceRefresh: Boolean = false) { - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { noteRepository.getNotes(it, 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) - } - }.subscribe({ - view?.apply { - updateData(it) - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - } - analytics.logEvent("load_note", mapOf("items" to it.size, "force_refresh" to forceRefresh)) - }, { - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - ) - } - - fun onNoteItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is NoteItem) { - view?.run { - showNoteDialog(item.note) - if (!item.note.isRead) { - item.note.isRead = true - updateItem(item) - updateNote(item.note) - } - } - } - } - - private fun updateNote(note: Note) { - disposable.add(noteRepository.updateNote(note) - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ - Timber.d("Note ${note.id} updated") - }) { error -> 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 c060c8ed..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt +++ /dev/null @@ -1,28 +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 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 7273e8e9..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt +++ /dev/null @@ -1,74 +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.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) - } - - 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 40368913..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings - -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.services.job.ServiceHelper -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 javax.inject.Inject - -class SettingsPresenter @Inject constructor( - errorHandler: ErrorHandler, - private val preferencesRepository: PreferencesRepository, - private val serviceHelper: ServiceHelper, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: SettingsView) { - super.onAttachView(view) - - view.run { - setServicesSuspended(preferencesRepository.serviceEnablesKey, now().isHolidays) - } - } - - fun onSharedPreferenceChanged(key: String) { - when (key) { - preferencesRepository.serviceEnablesKey -> { - if (preferencesRepository.isServiceEnabled) serviceHelper.startFullSyncService() - else serviceHelper.stopFullSyncService() - } - preferencesRepository.servicesIntervalKey, - preferencesRepository.servicesOnlyWifiKey -> { - serviceHelper.reloadFullSyncService() - } - preferencesRepository.currentThemeKey -> { - view?.setTheme(preferencesRepository.currentTheme) - } - } - - analytics.logEvent("setting_changed", mapOf("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 0b3c2f70..00000000 --- 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 223d7abe..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.ui.modules.splash - -import android.os.Bundle -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 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 5f8ba16e..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.ui.modules.splash - -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import javax.inject.Inject - -class SplashPresenter @Inject constructor( - private val studentRepository: StudentRepository, - errorHandler: ErrorHandler -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: SplashView) { - super.onAttachView(view) - view.run { - if (studentRepository.isStudentSaved) openMainView() - else openLoginView() - } - } -} 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 9efd8123..00000000 --- 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 743f37e5..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt +++ /dev/null @@ -1,78 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -import android.annotation.SuppressLint -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.Timetable -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_timetable.* - -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) - } - - @SuppressLint("SetTextI18n") - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - timetableDialogSubject.text = lesson.subject - timetableDialogTime.text = "${lesson.start.toFormattedString("HH:mm")} - ${lesson.end.toFormattedString("HH:mm")}" - - lesson.group.let { - if (it.isBlank()) { - timetableDialogGroupTitle.visibility = GONE - timetableDialogGroup.visibility = GONE - } else timetableDialogGroup.text = it - } - - lesson.room.let { - if (it.isBlank()) { - timetableDialogRoomTitle.visibility = GONE - timetableDialogRoom.visibility = GONE - } else timetableDialogRoom.text = it - } - - lesson.teacher.let { - if (it.isBlank()) { - timetableDialogTeacherTitle.visibility = GONE - timetableDialogTeacher.visibility = GONE - } else timetableDialogTeacher.text = it - } - - lesson.info.let { - if (it.isBlank()) { - timetableDialogChangesTitle.visibility = GONE - timetableDialogChanges.visibility = GONE - } else timetableDialogChanges.text = it - } - - timetableDialogClose.setOnClickListener { dismiss() } - } -} 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 faf17665..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt +++ /dev/null @@ -1,122 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -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.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.MainView -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 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 - } - timetableSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - timetablePreviousButton.setOnClickListener { presenter.onPreviousDay() } - timetableNextButton.setOnClickListener { presenter.onNextDay() } - } - - 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 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 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) { - TimetableDialog.newInstance(lesson).show(fragmentManager, lesson.toString()) - } - - 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 f37c0f51..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt +++ /dev/null @@ -1,62 +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(): Int = 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 { - return lesson.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/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt deleted file mode 100644 index 27a9db99..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ /dev/null @@ -1,112 +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.SemesterRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.repositories.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 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) - view.initView() - loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay())) - reloadView() - } - - fun onPreviousDay() { - loadData(currentDate.previousSchoolDay) - reloadView() - } - - fun onNextDay() { - loadData(currentDate.nextSchoolDay) - reloadView() - } - - fun onSwipeRefresh() { - loadData(currentDate, true) - } - - fun onViewReselected() { - now().nextOrSameSchoolDay.also { - if (currentDate != it) { - loadData(it) - reloadView() - } else if (view?.isViewEmpty == false) view?.resetView() - } - } - - fun onTimetableItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is TimetableItem) view?.showTimetableDialog(item.lesson) - } - - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - 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) - } - } - .subscribe({ - view?.apply { - updateData(it) - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - } - analytics.logEvent("load_attendance", mapOf("items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd"))) - }) { - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } - } - - private fun reloadView() { - view?.apply { - showProgress(true) - showContent(false) - showEmpty(false) - clearData() - showNextButton(!currentDate.plusDays(1).isHolidays) - showPreButton(!currentDate.minusDays(1).isHolidays) - updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.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 0ad0eaeb..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt +++ /dev/null @@ -1,35 +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 - - 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 showContent(show: Boolean) - - fun showPreButton(show: Boolean) - - fun showNextButton(show: Boolean) - - fun showTimetableDialog(lesson: Timetable) -} 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 00000000..c9f98d5e --- /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 00000000..a69deb03 --- /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 00000000..c3d81215 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.java @@ -0,0 +1,29 @@ +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; + +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 { + getView().openMainActivity(); + } + } +} 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 fecdc42e..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt +++ /dev/null @@ -1,95 +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.SemesterRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.repositories.TimetableRepository -import io.github.wulkanowy.utils.toFormattedString -import io.reactivex.disposables.CompositeDisposable -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 context: Context, - private val intent: Intent? -) : RemoteViewsService.RemoteViewsFactory { - - private val disposable: CompositeDisposable = CompositeDisposable() - - 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 onDataSetChanged() { - intent?.action?.let { LocalDate.ofEpochDay(sharedPref.getLong(it, 0)) } - ?.let { date -> - if (studentRepository.isStudentSaved) { - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { timetableRepository.getTimetable(it, date, date) } - .map { item -> item.sortedBy { it.number } } - .subscribe({ lessons = it }) - { Timber.e(it, "An error has occurred while downloading data for the widget") }) - } - } - } - - override fun getViewAt(position: Int): RemoteViews? { - if (position == INVALID_POSITION) 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.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()) - } - } - - override fun onDestroy() { - disposable.clear() - } -} 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 a8ead8fb..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt +++ /dev/null @@ -1,130 +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_UPDATE -import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS -import android.appwidget.AppWidgetProvider -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 javax.inject.Inject - -class TimetableWidgetProvider : AppWidgetProvider() { - - @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" - } - - override fun onUpdate(context: Context?, appWidgetManager: AppWidgetManager?, appWidgetIds: IntArray?) { - appWidgetIds?.forEach { - val widgetKey = "timetable_widget_$it" - checkSavedWidgetDate(widgetKey) - - val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(widgetKey, 0)) - context?.run { - RemoteViews(packageName, R.layout.widget_timetable).apply { - setTextViewText(R.id.timetableWidgetDay, savedDate.weekDayName.capitalize()) - setTextViewText(R.id.timetableWidgetDate, savedDate.toFormattedString()) - setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty) - setRemoteAdapter(R.id.timetableWidgetList, Intent(context, TimetableWidgetService::class.java) - .apply { action = widgetKey }) - setOnClickPendingIntent(R.id.timetableWidgetNext, createNavIntent(context, it, it, appWidgetIds, BUTTON_NEXT)) - setOnClickPendingIntent(R.id.timetableWidgetPrev, createNavIntent(context, -it, it, appWidgetIds, BUTTON_PREV)) - createNavIntent(context, Int.MAX_VALUE, it, appWidgetIds, BUTTON_RESET).let { intent -> - setOnClickPendingIntent(R.id.timetableWidgetDate, intent) - setOnClickPendingIntent(R.id.timetableWidgetDay, intent) - } - setPendingIntentTemplate(R.id.timetableWidgetList, - PendingIntent.getActivity(context, 1, MainActivity.getStartIntent(context).apply { - putExtra(EXTRA_START_MENU_INDEX, 3) - }, FLAG_UPDATE_CURRENT)) - - }.also { view -> - appWidgetManager?.apply { - notifyAppWidgetViewDataChanged(it, R.id.timetableWidgetList) - updateAppWidget(it, view) - } - } - } - } - } - - override fun onReceive(context: Context?, intent: Intent?) { - AndroidInjection.inject(this, context) - intent?.let { - val widgetKey = "timetable_widget_${it.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0)}" - it.getStringExtra(EXTRA_BUTTON_TYPE).let { button -> - when (button) { - BUTTON_NEXT -> { - LocalDate.ofEpochDay(sharedPref.getLong(widgetKey, 0)).nextSchoolDay - .let { date -> sharedPref.putLong(widgetKey, date.toEpochDay(), true) } - } - BUTTON_PREV -> { - LocalDate.ofEpochDay(sharedPref.getLong(widgetKey, 0)).previousSchoolDay - .let { date -> sharedPref.putLong(widgetKey, date.toEpochDay(), true) } - } - BUTTON_RESET -> sharedPref.putLong(widgetKey, LocalDate.now().nextOrSameSchoolDay.toEpochDay(), true) - } - button?.also { btn -> - if (btn.isNotBlank()) { - analytics.logEvent("changed_timetable_widget_day", mapOf("button" to button)) - } - } - } - } - super.onReceive(context, intent) - } - - override fun onDeleted(context: Context?, appWidgetIds: IntArray?) { - appWidgetIds?.forEach { - sharedPref.delete("timetable_widget_$it") - } - } - - private fun createNavIntent(context: Context, code: Int, widgetId: Int, widgetIds: IntArray, buttonType: String): PendingIntent { - return PendingIntent.getBroadcast(context, code, - Intent(context, TimetableWidgetProvider::class.java).apply { - action = ACTION_APPWIDGET_UPDATE - putExtra(EXTRA_APPWIDGET_IDS, widgetIds) - putExtra(EXTRA_BUTTON_TYPE, buttonType) - putExtra(EXTRA_TOGGLED_WIDGET_ID, widgetId) - }, FLAG_UPDATE_CURRENT) - } - - private fun checkSavedWidgetDate(widgetKey: String) { - sharedPref.run { - if (getLong(widgetKey, -1) == -1L) { - putLong(widgetKey, LocalDate.now().nextOrSameSchoolDay.toEpochDay(), true) - } - } - } -} 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 c0314d02..00000000 --- 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 00000000..27d76e75 --- /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 c2b1efaa..00000000 --- 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 00000000..cc33e65a --- /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 00000000..8ec73faf --- /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 e056bf7e..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt +++ /dev/null @@ -1,15 +0,0 @@ -package io.github.wulkanowy.utils - -import android.content.Context -import androidx.annotation.AttrRes -import androidx.annotation.ColorInt - -@ColorInt -fun Context.getThemeAttrColor(@AttrRes colorAttr: Int): Int { - val array = this.obtainStyledAttributes(null, intArrayOf(colorAttr)) - try { - return array.getColor(0, 0) - } finally { - array.recycle() - } -} 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 00000000..1dd5f641 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java @@ -0,0 +1,106 @@ +package io.github.wulkanowy.utils; + + +import java.util.ArrayList; +import java.util.List; + +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.Lesson; +import io.github.wulkanowy.data.db.dao.entities.Subject; +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()) + .setFreeDay(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 Lesson lessonToLessonEntity(io.github.wulkanowy.api.generic.Lesson lesson) { + return new Lesson() + .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 List lessonsToLessonsEntities(List lessonList) { + + List lessonEntityList = new ArrayList<>(); + + for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) { + lessonEntityList.add(lessonToLessonEntity(lesson)); + } + return lessonEntityList; + } +} 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 00000000..5681bad1 --- /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 53f9197f..00000000 --- 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, params: Map) { - Bundle().apply { - params.forEach { - if (it.value == null) return@forEach - when (it.value) { - is String, is String? -> putString(it.key, it.value as String) - is Int, is Int? -> putInt(it.key, it.value as Int) - is Boolean, is Boolean? -> putBoolean(it.key, it.value 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 bd6867a3..00000000 --- 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 dcf47754..00000000 --- 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 638bf364..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt +++ /dev/null @@ -1,60 +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 } -} - -inline val Grade.valueColor: Int - get() { - return when (value) { - 6 -> R.color.grade_six - 5 -> R.color.grade_five - 4 -> R.color.grade_four - 3 -> R.color.grade_three - 2 -> R.color.grade_two - 1 -> R.color.grade_one - else -> R.color.grade_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 { - if (modifier != 0.0) { - if (plusModifier != 0.0 && modifier > 0) { - modifier = plusModifier - } - if (minusModifier != .0 && modifier < 0) { - modifier = -minusModifier - } - } - return 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 00000000..6be1b76f --- /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 d21b998b..00000000 --- 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 00000000..f3b5b3ec --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/LogUtils.java @@ -0,0 +1,22 @@ +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); + } +} 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 56537413..00000000 --- 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 00000000..a1484eb9 --- /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 00000000..9c23c217 --- /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 e426d4d5..00000000 --- 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 bea2bd3d..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/SpinnerExtension.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.wulkanowy.utils - -import android.view.View -import android.widget.AdapterView -import android.widget.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) { - 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 3cd38c73..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt +++ /dev/null @@ -1,121 +0,0 @@ -package io.github.wulkanowy.utils - -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)) - -/** - * 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 00000000..717ed42a --- /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 6a5ad880..00000000 --- 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 00000000..5106d641 --- /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 00000000..0d33c57c --- /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 00000000..1ee4a9fa --- /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 00000000..93da1d0b --- /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 24e6d3ff..00000000 --- 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 59f830fa..00000000 --- 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/contactEmail b/app/src/main/play/contactEmail deleted file mode 100644 index 851521eb..00000000 --- a/app/src/main/play/contactEmail +++ /dev/null @@ -1 +0,0 @@ -wulkanowyinc@gmail.com diff --git a/app/src/main/play/contactPhone b/app/src/main/play/contactPhone deleted file mode 100644 index 552e53a5..00000000 --- a/app/src/main/play/contactPhone +++ /dev/null @@ -1 +0,0 @@ -+48733393622 diff --git a/app/src/main/play/contactWebsite b/app/src/main/play/contactWebsite deleted file mode 100644 index 53f7bee6..00000000 --- a/app/src/main/play/contactWebsite +++ /dev/null @@ -1 +0,0 @@ -https://wulkanowy.github.io/ diff --git a/app/src/main/play/defaultLanguage b/app/src/main/play/defaultLanguage deleted file mode 100644 index 1134c4d5..00000000 --- a/app/src/main/play/defaultLanguage +++ /dev/null @@ -1 +0,0 @@ -pl-PL diff --git a/app/src/main/play/pl-PL/listing/featureGraphic/feature.png b/app/src/main/play/pl-PL/listing/featureGraphic/feature.png deleted file mode 100644 index 094b25b6..00000000 Binary files a/app/src/main/play/pl-PL/listing/featureGraphic/feature.png and /dev/null differ diff --git a/app/src/main/play/pl-PL/listing/fulldescription b/app/src/main/play/pl-PL/listing/fulldescription deleted file mode 100644 index 7c1f99fd..00000000 --- a/app/src/main/play/pl-PL/listing/fulldescription +++ /dev/null @@ -1,13 +0,0 @@ -Aplikacja jest we wczesnej fazie rozwoju, ciągle pracujemy nad kolejnymi funkcjami. - -Wyróżnione cechy i funkcje: -- brak reklam, -- tryb offline, -- powiadomienia, -- obliczanie średniej ważonej, -- procentowy podgląd frekwencji, -- ciemny motyw. - -GitHub: https://github.com/wulkanowy/wulkanowy -Discord: https://discord.gg/vccAQBr -Trello: https://trello.com/b/A97NUM1s/wulkanowy diff --git a/app/src/main/play/pl-PL/listing/icon/icon.png b/app/src/main/play/pl-PL/listing/icon/icon.png deleted file mode 100644 index de216d36..00000000 Binary files a/app/src/main/play/pl-PL/listing/icon/icon.png and /dev/null differ diff --git a/app/src/main/play/pl-PL/listing/phoneScreenshots/account-switcher.png b/app/src/main/play/pl-PL/listing/phoneScreenshots/account-switcher.png deleted file mode 100644 index 8a52d8de..00000000 Binary files a/app/src/main/play/pl-PL/listing/phoneScreenshots/account-switcher.png and /dev/null differ diff --git a/app/src/main/play/pl-PL/listing/phoneScreenshots/attendance-dialog.png b/app/src/main/play/pl-PL/listing/phoneScreenshots/attendance-dialog.png deleted file mode 100644 index c6508574..00000000 Binary files a/app/src/main/play/pl-PL/listing/phoneScreenshots/attendance-dialog.png and /dev/null differ diff --git a/app/src/main/play/pl-PL/listing/phoneScreenshots/attendance-statistics.png b/app/src/main/play/pl-PL/listing/phoneScreenshots/attendance-statistics.png deleted file mode 100644 index 376fc3c3..00000000 Binary files a/app/src/main/play/pl-PL/listing/phoneScreenshots/attendance-statistics.png and /dev/null differ diff --git a/app/src/main/play/pl-PL/listing/phoneScreenshots/grades.png b/app/src/main/play/pl-PL/listing/phoneScreenshots/grades.png deleted file mode 100644 index 6b4089b0..00000000 Binary files a/app/src/main/play/pl-PL/listing/phoneScreenshots/grades.png and /dev/null differ diff --git a/app/src/main/play/pl-PL/listing/phoneScreenshots/more.png b/app/src/main/play/pl-PL/listing/phoneScreenshots/more.png deleted file mode 100644 index c3dc523f..00000000 Binary files a/app/src/main/play/pl-PL/listing/phoneScreenshots/more.png and /dev/null differ diff --git a/app/src/main/play/pl-PL/listing/phoneScreenshots/timetable-dialog.png b/app/src/main/play/pl-PL/listing/phoneScreenshots/timetable-dialog.png deleted file mode 100644 index 511433d4..00000000 Binary files a/app/src/main/play/pl-PL/listing/phoneScreenshots/timetable-dialog.png and /dev/null differ diff --git a/app/src/main/play/pl-PL/listing/phoneScreenshots/timetable-widget.png b/app/src/main/play/pl-PL/listing/phoneScreenshots/timetable-widget.png deleted file mode 100644 index 1d502470..00000000 Binary files a/app/src/main/play/pl-PL/listing/phoneScreenshots/timetable-widget.png and /dev/null differ diff --git a/app/src/main/play/pl-PL/listing/shortdescription b/app/src/main/play/pl-PL/listing/shortdescription deleted file mode 100644 index c850c159..00000000 --- a/app/src/main/play/pl-PL/listing/shortdescription +++ /dev/null @@ -1 +0,0 @@ -Nieoficjalna aplikacja ucznia i rodzica dla dziennika VULCAN UONET+ diff --git a/app/src/main/play/pl-PL/listing/title b/app/src/main/play/pl-PL/listing/title deleted file mode 100644 index 2d53a62a..00000000 --- a/app/src/main/play/pl-PL/listing/title +++ /dev/null @@ -1 +0,0 @@ -Wulkanowy Dzienniczek diff --git a/app/src/main/play/pl-PL/listing/video b/app/src/main/play/pl-PL/listing/video deleted file mode 100644 index e69de29b..00000000 diff --git a/app/src/main/play/pl-PL/whatsnew b/app/src/main/play/pl-PL/whatsnew deleted file mode 100644 index e6916874..00000000 --- a/app/src/main/play/pl-PL/whatsnew +++ /dev/null @@ -1,16 +0,0 @@ -Wersja 0.6.0 - -Aplikacja została całkowicie przepisana! -Przez wielkie zmian, które w niej zaszły, wymagane jest ponowne logowanie w aplikacji -oraz ponowne dodanie widgetu planu lekcji do ekranu głównego. - -Mamy nową ikonę aplikacji! 🎉 - -Dodaliśmy nowe funkcje: -- przeglądanie wiadomości (wysyłanie pojawi się w przyszłości) -- zadania domowe -- statystyki frekwencji -- menadżer kont -- możliwość zmiany wagi plusów i minusów - -Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.6.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 00000000..34544980 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 ea5b85c8..00000000 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_message.png b/app/src/main/res/drawable-hdpi/ic_stat_notify_message.png deleted file mode 100644 index 86d63c58..00000000 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 b49e4ad2..00000000 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 00000000..5564049a 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 64fd285d..00000000 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_message.png b/app/src/main/res/drawable-mdpi/ic_stat_notify_message.png deleted file mode 100644 index 73a1653a..00000000 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 3498f71c..00000000 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 ca44eb29..00000000 --- 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 64812f15..c58c374a 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 61489d81..00000000 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 5fa58e90..00000000 --- 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 00000000..611bb2a7 --- /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 00000000..fabb8760 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 83b2c443..00000000 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_message.png b/app/src/main/res/drawable-xhdpi/ic_stat_notify_message.png deleted file mode 100644 index b373ffd4..00000000 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 5aa30e5f..00000000 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 00000000..c1d78d46 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 cba48ec2..00000000 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_message.png b/app/src/main/res/drawable-xxhdpi/ic_stat_notify_message.png deleted file mode 100644 index dc15bbd9..00000000 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 db3ec517..00000000 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 00000000..ee89e601 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 3b40ad3f..00000000 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 06a9299a..00000000 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 8ba66975..00000000 --- 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 f4f1f2c5..1c41b556 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 550f312e..00000000 --- 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 dea10cb3..00000000 --- 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_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 f46b3ad7..dc33b8aa 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 988df22c..4d3c91cf 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 cfec6390..c5ecc747 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_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 eccc338f..fdef7322 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_grade_semester_24dp.xml b/app/src/main/res/drawable/ic_menu_dashboard_24dp.xml similarity index 61% rename from app/src/main/res/drawable/ic_menu_grade_semester_24dp.xml rename to app/src/main/res/drawable/ic_menu_dashboard_24dp.xml index dbc32c27..50726abd 100644 --- a/app/src/main/res/drawable/ic_menu_grade_semester_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_dashboard_24dp.xml @@ -4,6 +4,6 @@ android:viewportHeight="24.0" android:viewportWidth="24.0"> + android:fillColor="#ffffff" + android:pathData="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z" /> 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 3866cd3c..61506dad 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_main_exam_24dp.xml b/app/src/main/res/drawable/ic_menu_main_exam_24dp.xml deleted file mode 100644 index 13179b7c..00000000 --- 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 449f0e28..00000000 --- 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_more_24dp.xml b/app/src/main/res/drawable/ic_menu_main_more_24dp.xml deleted file mode 100644 index 26932b2b..00000000 --- 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 6b3172ba..00000000 --- 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 1594285f..00000000 --- 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 00000000..9fed586b --- /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_timetable_24dp.xml b/app/src/main/res/drawable/ic_menu_timetable_24dp.xml new file mode 100644 index 00000000..a689dc06 --- /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_more_messages_24dp.xml b/app/src/main/res/drawable/ic_more_messages_24dp.xml deleted file mode 100644 index 65f32169..00000000 --- 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 fc12cdcf..00000000 --- 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_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 34345521..00000000 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 00000000..8f3a86fa --- /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 00000000..9d8f403c 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 fb521bf6..00000000 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 60a35ab6..00000000 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 00000000..0b79b672 --- /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 1a86fa92..67095982 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"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +