diff --git a/.bettercodehub.yml b/.bettercodehub.yml index cce850d7..349f7675 100644 --- a/.bettercodehub.yml +++ b/.bettercodehub.yml @@ -1,5 +1,3 @@ -exclude: -- /app/src/main/java/io/github/wulkanowy/data/db/dao/entities/.* -component_depth: 1 +component_depth: 8 languages: -- java +- kotlin diff --git a/.circleci/config.yml b/.circleci/config.yml index 387c5edd..cad32112 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,11 +7,11 @@ references: container_config: &container_config docker: - - image: circleci/android:api-27-alpha + - image: circleci/android@sha256:5cdc8626cc6f13efe5ed982cdcdb432b0472f8740fed8743a6461e025ad6cdfc working_directory: *workspace_root environment: environment: - JVM_OPTS: -Xmx3200m + _JAVA_OPTS: -Xmx3072m attach_workspace: &attach_workspace attach_workspace: @@ -25,27 +25,20 @@ 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 - run: name: Setup environment command: ./gradlew dependencies --no-daemon --stacktrace --console=plain -PdisablePreDex || true - - run: - name: Decrypt keys - command: | - 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: Initial build - command: ./gradlew build assembleDebug -x test -x lint -x fabricGenerateResourcesRelease --no-daemon --stacktrace --console=plain -PdisablePreDex + command: ./gradlew build -x test -x lint -x fabricGenerateResourcesFdroidRelease -x fabricGenerateResourcesPlayRelease -x packageRelease --no-daemon --stacktrace --console=plain -PdisablePreDex - run: - name: Clear keys - command: | - rm ./app/key.p12 ./app/upload-key.jks - - store_artifacts: - path: ./app/build/outputs/apk/ - destination: apks/ + name: Run FOSSA + command: fossa --no-ansi || true - persist_to_workspace: root: *workspace_root paths: @@ -63,7 +56,7 @@ jobs: <<: *general_cache_key - run: name: Run lint - command: ./gradlew lint -x fabricGenerateResourcesRelease --no-daemon --stacktrace --console=plain -PdisablePreDex + command: ./gradlew lint -x fabricGenerateResourcesFdroidRelease -x fabricGenerateResourcesPlayRelease --no-daemon --stacktrace --console=plain -PdisablePreDex - store_artifacts: path: ./app/build/reports/ destination: lint_reports/app/ @@ -82,7 +75,7 @@ jobs: <<: *general_cache_key - run: name: Run app tests - command: ./gradlew :app:test :app:jacocoTestReport -x fabricGenerateResourcesRelease --no-daemon --stacktrace --console=plain -PdisablePreDex + command: ./gradlew :app:test :app:jacocoTestReport -x fabricGenerateResourcesFdroidRelease -x fabricGenerateResourcesPlayRelease --no-daemon --stacktrace --console=plain -PdisablePreDex - run: name: Upload unit code coverage to codecov command: bash <(curl -s https://codecov.io/bash) -F app @@ -96,35 +89,16 @@ 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/ - destination: 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: Accept licenses + command: yes | sdkmanager --licenses && yes | sdkmanager --update - run: name: Setup emulator - command: sdkmanager "system-images;android-16;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-16;default;armeabi-v7a" + command: sdkmanager "system-images;android-19;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-19;default;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 @@ -140,15 +114,13 @@ 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 createDebugCoverageReport --no-daemon --stacktrace --console=plain -PdisablePreDex + command: ./gradlew clean createPlayDebugCoverageReport jacocoTestReport --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 @@ -173,7 +145,8 @@ jobs: command: ./gradlew jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex - run: name: Run sonarqube runner - 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 + 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 @@ -184,11 +157,12 @@ jobs: - 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 -PdisablePreDex + command: ./gradlew publishPlayRelease --no-daemon --stacktrace --console=plain -PenableCrashlytics -PdisablePreDex workflows: version: 2 @@ -211,12 +185,6 @@ workflows: only: /.*/ requires: - build - - api-test: - filters: - tags: - only: /.*/ - requires: - - build - instrumented: filters: tags: @@ -231,7 +199,6 @@ workflows: - build - lint - app-test - - api-test - instrumented - deploy: requires: diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..27d57f59 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,17 @@ +## 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 8ad04ebf..63783326 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,6 @@ local.properties .idea/vcs.xml .idea/workspace.xml .idea/caches/ -.idea/codeStyles/ *.iml # OS-specific files @@ -44,7 +43,9 @@ local.properties .Trashes ehthumbs.db Thumbs.db -.idea/codeStyles/ .idea/caches/ -./app/key.p12 -./app/upload-key.jks +app/key.p12 +app/upload-key.jks +*.log +.idea/assetWizardSettings.xml +.idea/uiDesigner.xml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bf80ae04..a84ac839 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: circleci/android:api-27-alpha +image: circleci/android:api-28-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/fdroid/debug/app-fdroid-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 -x fabricGenerateResourcesFdroidRelease -x fabricGenerateResourcesPlayRelease 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 -x fabricGenerateResourcesFdroidRelease -x fabricGenerateResourcesPlayRelease lint artifacts: paths: - app/build/reports diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml deleted file mode 100644 index f5bd97e6..00000000 --- a/.idea/codeStyleSettings.xml +++ /dev/null @@ -1,226 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..43e9b544 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,147 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..0f7bc519 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 48777522..7ac24c77 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -8,7 +8,6 @@ diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460d..00000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..4c40f615 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,67 @@ +language: android +jdk: oraclejdk8 + +env: + global: + - ANDROID_API_LEVEL=28 + - ANDROID_BUILD_TOOLS_VERSION=28.0.3 + +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ + +#branches: +# only: +# - develop + +android: + licenses: + - android-sdk-preview-license-.+ + - android-sdk-license-.+ + - google-gdk-license-.+ + components: + - tools + - platform-tools + # The BuildTools version used by your project + - build-tools-$ANDROID_BUILD_TOOLS_VERSION + # The SDK version used to compile your project + - android-$ANDROID_API_LEVEL + # Additional components + - extra-google-google_play_services + - extra-google-m2repository + - extra-android-m2repository + - addon-google_apis-google-$ANDROID_API_LEVEL + # Android emulator + - android-19 + - sys-img-armeabi-v7a-android-19 + +before_script: + # Launch emulator before the execution + - echo no | android create avd --force -n test -t android-19 --abi armeabi-v7a + - emulator -avd test -no-audio -no-window & + - android-wait-for-emulator + - adb shell input keyevent 82 & + - "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash" + +script: + - ./gradlew dependencies --stacktrace --daemon + - fossa --no-ansi || true + #- ./gradlew lintPlayRelease -x fabricGenerateResourcesPlayRelease --stacktrace --daemon + - ./gradlew testPlayDebugUnitTest -x fabricGenerateResourcesPlay --stacktrace --daemon + - ./gradlew createPlayDebugCoverageReport --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 fabricGenerateResourcesPlayRelease -x fabricGenerateResourcesFdroidRelease -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 publishPlayRelease -PenableCrashlytics --stacktrace; + fi + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/README.md b/README.md index ee11a9c6..07f06d7f 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,23 @@ # Wulkanowy -[![CircleCI](https://img.shields.io/circleci/project/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://circleci.com/gh/wulkanowy/wulkanowy) +[![Travis](https://img.shields.io/travis/com/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://travis-ci.com/wulkanowy/wulkanowy) [![Bitrise](https://img.shields.io/bitrise/daeff1893f3c8128/master.svg?token=Hjm1ACamk86JDeVVJHOeqQ&style=flat-square)](https://www.bitrise.io/app/daeff1893f3c8128) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![BCH compliance](https://bettercodehub.com/edge/badge/wulkanowy/wulkanowy?branch=master)](https://bettercodehub.com/) -[![Scrutinizer](https://img.shields.io/scrutinizer/g/wulkanowy/wulkanowy.svg?style=flat-square)](https://scrutinizer-ci.com/g/wulkanowy/wulkanowy/?branch=master) -[![Bintray](https://img.shields.io/bintray/v/wulkanowy/wulkanowy/api.svg?style=flat-square)](https://bintray.com/wulkanowy/wulkanowy/api) +[![Sonarcloud](https://sonarcloud.io/api/project_badges/measure?project=io.github.wulkanowy%3Aapp&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=io.github.wulkanowy%3Aapp) +[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy.svg?type=shield)](https://app.fossa.com/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy?ref=badge_shield) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) +[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg)](https://f-droid.org/packages/io.github.wulkanowy/) +[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github)](https://github.com/wulkanowy/wulkanowy/releases) -[Pobierz wersję beta](https://play.google.com/store/apps/details?id=io.github.wulkanowy&utm_source=vcs) +[Pobierz wersję beta z Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy&utm_source=vcs) -[Pobierz wersję DEV](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/0) +[Pobierz wersję DEV](https://bitrise-redirector.herokuapp.com/v0.1/apps/f841f20d8f8b1dc8/builds/master/artifacts/0) +[(Więcej wersji DEV)](https://wulkanowy.github.io/dev.html) Androidowy klient dziennika VULCAN UONET+. + + +## License + +[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy.svg?type=large)](https://app.fossa.com/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy?ref=badge_large) diff --git a/api/build.gradle b/api/build.gradle deleted file mode 100644 index 5e5eeee3..00000000 --- a/api/build.gradle +++ /dev/null @@ -1,121 +0,0 @@ -apply plugin: 'java-library' -apply plugin: 'kotlin' -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:$jsoup" - implementation "org.apache.commons:commons-lang3:$apacheLang" - implementation "com.google.code.gson:gson:$gson" - implementation "org.slf4j:slf4j-api:$slf4jApi" - - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - - testImplementation "junit:junit:$junit" - testImplementation "org.mockito:mockito-core:$mockito" -} - -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 deleted file mode 100644 index c6360d04..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/Client.java +++ /dev/null @@ -1,255 +0,0 @@ -package io.github.wulkanowy.api; - -import org.jsoup.Connection; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import io.github.wulkanowy.api.generic.School; -import io.github.wulkanowy.api.login.Login; - -public class Client { - - private String protocol = "https"; - - private String host = "vulcan.net.pl"; - - private String email; - - private String password; - - private String symbol; - - private String schoolId; - - private List schools; - - private Date lastSuccessRequest; - - private Cookies cookies = new Cookies(); - - private static final Logger logger = LoggerFactory.getLogger(Client.class); - - Client(String email, String password, String symbol, String schoolId) { - this.email = email; - this.password = password; - this.symbol = symbol; - this.schoolId = schoolId; - - setFullEndpointInfo(email); - } - - private void setFullEndpointInfo(String info) { - String[] creds = info.split("\\\\"); - - email = info; - - if (creds.length > 2) { - String[] url = creds[0].split("://"); - - protocol = url[0]; - String[] path = url[1].split("/"); - host = path[0]; - if (path.length > 1) { - symbol = path[1]; - } - email = creds[2]; - } - } - - private void login() throws IOException, VulcanException { - if (isLoggedIn()) { - return; - } - - logger.info("Not logged. Login..."); - - clearCookies(); - new Login(this).login(email, password, symbol); - lastSuccessRequest = new Date(); - - logger.info("Login successful on {} at {}", getHost(), new Date()); - } - - private boolean isLoggedIn() { - logger.trace("Last success request: {}", lastSuccessRequest); - logger.trace("Cookies: {}", getCookies().size()); - - return getCookies().size() > 0 && lastSuccessRequest != null && - 5 > TimeUnit.MILLISECONDS.toMinutes(new Date().getTime() - lastSuccessRequest.getTime()); - - } - - public String getSymbol() { - return symbol; - } - - public void setSymbol(String symbol) { - this.symbol = symbol; - } - - private Map getCookies() { - return cookies.getItems(); - } - - public void clearCookies() { - cookies = new Cookies(); - } - - public String getHost() { - return host; - } - - public void setSchools(List schools) { - this.schools = schools; - this.schoolId = schools.get(0).getId(); - } - - public List getSchools() throws IOException, VulcanException { - login(); - return schools; - } - - public String getSchoolId() throws IOException, VulcanException { - return schoolId != null ? schoolId : getSchools().get(0).getId(); - } - - String getFilledUrl(String url) { - return url - .replace("{schema}", protocol) - .replace("{host}", host) - .replace("{symbol}", symbol) - .replace("{ID}", schoolId != null ? schoolId : ""); - } - - public Document getPageByUrl(String url) throws IOException, VulcanException { - return getPageByUrl(url, true, null); - } - - public Document getPageByUrl(String url, boolean loginBefore) throws IOException, VulcanException { - return getPageByUrl(url, loginBefore, null); - } - - public synchronized Document getPageByUrl(String url, boolean loginBefore, Map cookies) throws IOException, VulcanException { - if (loginBefore) { - login(); - } - - if (null != cookies) { - this.cookies.addItems(cookies); - } - - url = getFilledUrl(url); - - logger.debug("GET {}", url); - - Connection.Response response = Jsoup.connect(url) - .followRedirects(true) - .cookies(getCookies()) - .execute(); - - this.cookies.addItems(response.cookies()); - - Document doc = checkForErrors(response.parse(), response.statusCode()); - - if (loginBefore) { - lastSuccessRequest = new Date(); - } - - return doc; - } - - public synchronized Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException { - url = getFilledUrl(url); - - logger.debug("POST {}", url); - - Connection connection = Jsoup.connect(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()); - - response.bufferUp(); // fixes cert parsing issues #109 - - return checkForErrors(response.parse(), response.statusCode()); - } - - public String getJsonStringByUrl(String url) throws IOException, VulcanException { - login(); - - url = getFilledUrl(url); - - logger.debug("GET {}", url); - - Connection.Response response = Jsoup.connect(url) - .followRedirects(true) - .ignoreContentType(true) - .cookies(getCookies()) - .execute(); - - this.cookies.addItems(response.cookies()); - - return response.body(); - } - - public String postJsonStringByUrl(String url, String[][] params) throws IOException, VulcanException { - login(); - - url = getFilledUrl(url); - - logger.debug("POST {}", url); - - Connection connection = Jsoup.connect(url); - - for (String[] data : params) { - connection.data(data[0], data[1]); - } - - Connection.Response response = connection - .followRedirects(true) - .ignoreContentType(true) - .method(Connection.Method.POST) - .cookies(getCookies()) - .execute(); - - this.cookies.addItems(response.cookies()); - - return response.body(); - } - - Document checkForErrors(Document doc, int code) throws VulcanException { - lastSuccessRequest = null; - - String title = doc.select("title").text(); - if ("Przerwa techniczna".equals(title)) { - throw new VulcanOfflineException(title); - } - - String singIn = doc.select(".loginButton").text(); - if ("Zaloguj się".equals(singIn)) { - throw new NotLoggedInErrorException(singIn); - } - - if ("Błąd strony".equals(title)) { - throw new NotLoggedInErrorException(title + " " + doc.body() + ", status: " + code); - } - - return doc; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/Cookies.java b/api/src/main/java/io/github/wulkanowy/api/Cookies.java deleted file mode 100644 index dfe4c4b5..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/Cookies.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.api; - -import java.util.HashMap; -import java.util.Map; - -class Cookies { - - private Map jar = new HashMap<>(); - - Map getItems() { - return jar; - } - - void addItems(Map items) { - jar.putAll(items); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/DateTimeUtils.kt b/api/src/main/java/io/github/wulkanowy/api/DateTimeUtils.kt deleted file mode 100644 index 3b620db5..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/DateTimeUtils.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.api - -import java.text.SimpleDateFormat -import java.util.* - -const val LOG_DATE_PATTERN = "dd.MM.yyyy" -const val API_DATE_PATTERN = "yyyy-MM-dd" - -const val TICKS_AT_EPOCH = 621355968000000000L -const val TICKS_PER_MILLISECOND = 10000 - -fun getFormattedDate(date: String): String { - return getFormattedDate(date, API_DATE_PATTERN) -} - -fun getFormattedDate(date: String, format: String): String { - return getFormattedDate(date, LOG_DATE_PATTERN, format) -} - -fun getFormattedDate(date: String, fromFormat: String, toFormat: String): String { - val sdf = SimpleDateFormat(fromFormat, Locale.ROOT) - val d = sdf.parse(date) - sdf.applyPattern(toFormat) - - return sdf.format(d) -} - -fun getDateAsTick(dateString: String?): String { - if (dateString.isNullOrEmpty()) { - return "" - } - - return getDateAsTick(dateString as String, API_DATE_PATTERN).toString() -} - -fun getDateAsTick(dateString: String, dateFormat: String): Long { - val format = SimpleDateFormat(dateFormat, Locale.ROOT) - format.timeZone = TimeZone.getTimeZone("UTC") - val dateObject = format.parse(dateString) - - return getDateAsTick(dateObject) -} - -fun getDateAsTick(date: Date): Long { - val calendar = Calendar.getInstance() - calendar.time = date - - return calendar.timeInMillis * TICKS_PER_MILLISECOND + TICKS_AT_EPOCH -} - -fun getDate(netTicks: Long): Date { - return Date((netTicks - TICKS_AT_EPOCH) / TICKS_PER_MILLISECOND) -} diff --git a/api/src/main/java/io/github/wulkanowy/api/NotLoggedInErrorException.java b/api/src/main/java/io/github/wulkanowy/api/NotLoggedInErrorException.java deleted file mode 100644 index 179fc8cc..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/NotLoggedInErrorException.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.wulkanowy.api; - -public class NotLoggedInErrorException extends VulcanException { - - public NotLoggedInErrorException(String message) { - super(message); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/SnP.java b/api/src/main/java/io/github/wulkanowy/api/SnP.java deleted file mode 100644 index 84ec1aed..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/SnP.java +++ /dev/null @@ -1,35 +0,0 @@ -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.generic.Diary; -import io.github.wulkanowy.api.generic.ParamItem; -import io.github.wulkanowy.api.generic.Semester; -import io.github.wulkanowy.api.generic.Student; - -public interface SnP { - - void setDiaryID(String id); - - String getStudentID(); - - List getStudents() throws IOException, VulcanException; - - StudentAndParent setUp() throws IOException, VulcanException; - - String getRowDataChildValue(Element e, int index); - - Document getSnPPageDocument(String url) throws IOException, VulcanException; - - List getDiaries() throws IOException, VulcanException; - - List getSemesters() throws IOException, VulcanException; - - List getSemesters(Document gradesPage); - - T getCurrent(List list); -} diff --git a/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java b/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java deleted file mode 100644 index ba527f6a..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java +++ /dev/null @@ -1,171 +0,0 @@ -package io.github.wulkanowy.api; - -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import io.github.wulkanowy.api.generic.Diary; -import io.github.wulkanowy.api.generic.ParamItem; -import io.github.wulkanowy.api.generic.Semester; -import io.github.wulkanowy.api.generic.Student; - -public class StudentAndParent implements SnP { - - 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 studentID; - - private String diaryID; - - private static final Logger logger = LoggerFactory.getLogger(StudentAndParent.class); - - StudentAndParent(Client client, String studentID, String diaryID) { - this.client = client; - this.studentID = studentID; - this.diaryID = diaryID; - } - - public StudentAndParent setUp() throws IOException, VulcanException { - if (null == getStudentID() || "".equals(getStudentID())) { - Document doc = client.getPageByUrl(BASE_URL); - - if (doc.select("#idSection").isEmpty()) { - logger.error("Expected SnP page, got page with title: {} {}", doc.title(), doc.selectFirst("body")); - throw new VulcanException("Nieznany błąd podczas pobierania danych. Strona: " + doc.title()); - } - - Student student = getCurrent(getStudents(doc)); - studentID = student.getId(); - - Diary diary = getCurrent(getDiaries(doc)); - diaryID = diary.getId(); - } - - return this; - } - - public String getStudentID() { - return studentID; - } - - public String getRowDataChildValue(Element e, int index) { - return e.select(".daneWiersz .wartosc").get(index - 1).text(); - } - - public void setDiaryID(String id) { - this.diaryID = id; - } - - public Document getSnPPageDocument(String url) throws IOException, VulcanException { - Map cookies = new HashMap<>(); - cookies.put("idBiezacyDziennik", diaryID); - cookies.put("idBiezacyUczen", studentID); - - Document doc = client.getPageByUrl(BASE_URL + url, true, cookies); - - if (!doc.title().startsWith("Witryna ucznia i rodzica")) { - logger.error("Expected SnP page, got page with title: {} {}", doc.title(), doc.selectFirst("body")); - throw new VulcanException("Nieznany błąd podczas pobierania danych. Strona: " + doc.title()); - } - - if (doc.title().endsWith("Strona główna")) { - throw new VulcanException("Sesja została nieprawidłowo zainicjowana"); - } - - return doc; - } - - public List getDiaries() throws IOException, VulcanException { - return getDiaries(client.getPageByUrl(BASE_URL)); - } - - private List getDiaries(Document doc) throws IOException, VulcanException { - return getList(doc.select("#dziennikDropDownList option"), Diary.class); - } - - public List getStudents() throws IOException, VulcanException { - return getStudents(client.getPageByUrl(BASE_URL)); - } - - private List getStudents(Document doc) throws IOException, VulcanException { - return getList(doc.select("#uczenDropDownList option"), Student.class); - } - - public List getSemesters() throws IOException, VulcanException { - return getSemesters(getSnPPageDocument(GRADES_PAGE_URL)); - } - - public List getSemesters(Document gradesPage) { - Elements semesterOptions = gradesPage.select("#okresyKlasyfikacyjneDropDownList option"); - - List semesters = new ArrayList<>(); - - for (Element e : semesterOptions) { - Semester semester = new Semester() - .setId(e.attr("value")) - .setName(e.text()); - - if (isCurrent(e)) { - semester.setCurrent(true); - } - - semesters.add(semester); - } - - return semesters; - } - - @SuppressWarnings("unchecked") - private List getList(Elements options, Class type) throws IOException, VulcanException { - List list = new ArrayList<>(); - - for (Element e : options) { - URL url = new URL(e.val()); - try { - ParamItem item = type.newInstance() - .setId(url.getQuery().split("=")[1]) - .setName(e.text()); - - if (isCurrent(e)) { - item.setCurrent(true); - } - - list.add((T) item); - } catch (Exception ex) { - throw new VulcanException("Error while trying to parse params list", ex); - } - } - - return list; - } - - @SuppressWarnings("unchecked") - public T getCurrent(List list) { - ParamItem current = null; - for (ParamItem s : list) { - if (s.isCurrent()) { - current = s; - break; - } - } - - return (T) current; - } - - private boolean isCurrent(Element e) { - return "selected".equals(e.attr("selected")); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/Vulcan.java b/api/src/main/java/io/github/wulkanowy/api/Vulcan.java deleted file mode 100644 index bf482969..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/Vulcan.java +++ /dev/null @@ -1,133 +0,0 @@ -package io.github.wulkanowy.api; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.List; - -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.generic.School; -import io.github.wulkanowy.api.grades.GradesList; -import io.github.wulkanowy.api.grades.SubjectsList; -import io.github.wulkanowy.api.messages.Messages; -import io.github.wulkanowy.api.mobile.RegisterDevice; -import io.github.wulkanowy.api.mobile.RegisteredDevices; -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 SnP snp; - - private Client client; - - private String studentId; - - private String diaryId; - - private static final Logger logger = LoggerFactory.getLogger(Vulcan.class); - - public void setCredentials(String email, String password, String symbol, String schoolId, String studentId, String diaryId) { - this.studentId = studentId; - this.diaryId = diaryId; - - client = new Client(email, password, symbol, schoolId); - - logger.debug("Client created with symbol " + symbol); - } - - public Client getClient() throws NotLoggedInErrorException { - if (null == client) { - throw new NotLoggedInErrorException("Vulcan must be initialized by calling setCredentials() prior to fetch data"); - } - - return client; - } - - public String getSymbol() throws NotLoggedInErrorException { - return getClient().getSymbol(); - } - - public List getSchools() throws VulcanException, IOException { - return getClient().getSchools(); - } - - public SnP getStudentAndParent() throws VulcanException, IOException { - if (null != this.snp) { - return this.snp; - } - - this.snp = new StudentAndParent(getClient(), studentId, diaryId) - .setUp(); - - return this.snp; - } - - public AttendanceTable getAttendanceTable() throws IOException, VulcanException { - return new AttendanceTable(getStudentAndParent()); - } - - public AttendanceStatistics getAttendanceStatistics() throws IOException, VulcanException { - return new AttendanceStatistics(getStudentAndParent()); - } - - public ExamsWeek getExamsList() throws IOException, VulcanException { - return new ExamsWeek(getStudentAndParent()); - } - - public GradesList getGradesList() throws IOException, VulcanException { - return new GradesList(getStudentAndParent()); - } - - public SubjectsList getSubjectsList() throws IOException, VulcanException { - return new SubjectsList(getStudentAndParent()); - } - - public AchievementsList getAchievementsList() throws IOException, VulcanException { - return new AchievementsList(getStudentAndParent()); - } - - public NotesList getNotesList() throws IOException, VulcanException { - return new NotesList(getStudentAndParent()); - } - - public SchoolInfo getSchoolInfo() throws IOException, VulcanException { - return new SchoolInfo(getStudentAndParent()); - } - - public TeachersInfo getTeachersInfo() throws IOException, VulcanException { - return new TeachersInfo(getStudentAndParent()); - } - - public Timetable getTimetable() throws IOException, VulcanException { - return new Timetable(getStudentAndParent()); - } - - public BasicInformation getBasicInformation() throws IOException, VulcanException { - return new BasicInformation(getStudentAndParent()); - } - - public FamilyInformation getFamilyInformation() throws IOException, VulcanException { - return new FamilyInformation(getStudentAndParent()); - } - - public RegisteredDevices getRegisteredDevices() throws VulcanException, IOException { - return new RegisteredDevices(getStudentAndParent()); - } - - public RegisterDevice getRegisterDevice() throws VulcanException, IOException { - return new RegisterDevice(getStudentAndParent()); - } - - public Messages getMessages() throws VulcanException { - return new Messages(getClient()); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/VulcanException.java b/api/src/main/java/io/github/wulkanowy/api/VulcanException.java deleted file mode 100644 index 0bc0c51f..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/VulcanException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.wulkanowy.api; - -public class VulcanException extends Exception { - - public VulcanException(String message) { - super(message); - } - - protected VulcanException(String message, Exception e) { - super(message, e); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/VulcanOfflineException.java b/api/src/main/java/io/github/wulkanowy/api/VulcanOfflineException.java deleted file mode 100644 index 24ab48e6..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/VulcanOfflineException.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.wulkanowy.api; - -public class VulcanOfflineException extends VulcanException { - - VulcanOfflineException(String message) { - super(message); - } -} 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 deleted file mode 100644 index 29b6a6d9..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceStatistics.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.wulkanowy.api.attendance; - -import org.apache.commons.lang3.math.NumberUtils; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.api.generic.Month; -import io.github.wulkanowy.api.generic.Subject; - -public class AttendanceStatistics { - - private SnP snp; - - private String attendancePageUrl = "Frekwencja.mvc"; - - public AttendanceStatistics(SnP snp) { - this.snp = snp; - } - - public Types getTypesTable() throws IOException, VulcanException { - return getTypesTable(""); - } - - public Types getTypesTable(String tick) throws IOException, VulcanException { - return getTypesTable(tick, -1); - } - - public List getSubjectList() throws IOException, VulcanException { - Element mainContainer = snp.getSnPPageDocument(attendancePageUrl) - .select(".mainContainer #idPrzedmiot").first(); - - List subjectList = new ArrayList<>(); - - for (Element subject : mainContainer.select("option")) { - subjectList.add(new Subject() - .setId(Integer.parseInt(subject.attr("value"))) - .setName(subject.text()) - ); - } - - return subjectList; - } - - public Types getTypesTable(String tick, Integer subjectId) throws IOException, VulcanException { - Element mainContainer = snp.getSnPPageDocument((attendancePageUrl - + "?data={tick}&idPrzedmiot={subject}") - .replace("{tick}", tick) - .replace("{subject}", subjectId.toString()) - ).select(".mainContainer").first(); - - Element table = mainContainer.select("table:nth-of-type(2)").first(); - - Elements headerCells = table.select("thead th"); - List typeList = new ArrayList<>(); - - Elements typesRows = table.select("tbody tr"); - - // fill types with months - for (Element row : typesRows) { - Elements monthsCells = row.select("td"); - - List monthList = new ArrayList<>(); - - // iterate over month in type, first column is empty, last is `total`; (0, n-1) - for (int i = 1; i < monthsCells.size() - 1; i++) { - monthList.add(new Month() - .setValue(NumberUtils.toInt(monthsCells.get(i).text(), 0)) - .setName(headerCells.get(i).text())); - } - - typeList.add(new Type() - .setTotal(NumberUtils.toInt(monthsCells.last().text(), 0)) - .setName(monthsCells.get(0).text()) - .setMonthList(monthList)); - } - - String total = mainContainer.select("h2").text().split(": ")[1]; - - return new Types() - .setTotal(NumberUtils.toDouble(total.replace("%", "").replace(",", "."))) - .setTypeList(typeList); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java deleted file mode 100644 index 7caa70d8..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java +++ /dev/null @@ -1,111 +0,0 @@ -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.VulcanException; -import io.github.wulkanowy.api.generic.Day; -import io.github.wulkanowy.api.generic.Lesson; -import io.github.wulkanowy.api.generic.Week; - -import static io.github.wulkanowy.api.DateTimeUtilsKt.getDateAsTick; -import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; - -public class AttendanceTable { - - private final static String ATTENDANCE_PAGE_URL = "Frekwencja.mvc?data="; - - private SnP snp; - - public AttendanceTable(SnP snp) { - this.snp = snp; - } - - public Week getWeekTable() throws IOException, VulcanException { - return getWeekTable(""); - } - - public Week getWeekTable(String date) throws IOException, VulcanException { - Element table = snp.getSnPPageDocument(ATTENDANCE_PAGE_URL + getDateAsTick(date)) - .select(".mainContainer .presentData").first(); - - Elements headerCells = table.select("thead th"); - List days = new ArrayList<>(); - - for (int i = 1; i < headerCells.size(); i++) { - String[] dayHeaderCell = headerCells.get(i).html().split("
"); - - days.add(new Day() - .setDayName(dayHeaderCell[0]) - .setDate(getFormattedDate(dayHeaderCell[1].trim())) - ); - } - - Elements hoursInDays = table.select("tbody tr"); - - // fill days in week with lessons - for (Element row : hoursInDays) { - Elements hours = row.select("td"); - - // fill hours in day - int size = hours.size(); - for (int i = 1; i < size; i++) { - Lesson lesson = new Lesson(); - lesson.setDate(days.get(i - 1).getDate()); - lesson.setNumber(Integer.valueOf(hours.get(0).text())); - - addLessonDetails(lesson, hours.get(i)); - - days.get(i - 1).setLesson(lesson); - } - } - - return new Week() - .setStartDayDate(days.get(0).getDate()) - .setDays(days); - } - - private void addLessonDetails(Lesson lesson, Element cell) { - lesson.setSubject(cell.select("span").text()); - - if (LessonTypes.CLASS_NOT_EXIST.equals(cell.attr("class"))) { - lesson.setNotExist(true); - lesson.setEmpty(true); - - return; - } - - switch (cell.select("div").attr("class")) { - case LessonTypes.CLASS_PRESENCE: - lesson.setPresence(true); - break; - case LessonTypes.CLASS_ABSENCE_UNEXCUSED: - lesson.setAbsenceUnexcused(true); - break; - case LessonTypes.CLASS_ABSENCE_EXCUSED: - lesson.setAbsenceExcused(true); - break; - case LessonTypes.CLASS_ABSENCE_FOR_SCHOOL_REASONS: - lesson.setAbsenceForSchoolReasons(true); - break; - case LessonTypes.CLASS_UNEXCUSED_LATENESS: - lesson.setUnexcusedLateness(true); - break; - case LessonTypes.CLASS_EXCUSED_LATENESS: - lesson.setExcusedLateness(true); - break; - case LessonTypes.CLASS_EXEMPTION: - lesson.setExemption(true); - break; - - default: - lesson.setEmpty(true); - break; - } - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/LessonTypes.java b/api/src/main/java/io/github/wulkanowy/api/attendance/LessonTypes.java deleted file mode 100644 index a9a1fac4..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/LessonTypes.java +++ /dev/null @@ -1,24 +0,0 @@ -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 deleted file mode 100644 index 6deb003e..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/Type.java +++ /dev/null @@ -1,42 +0,0 @@ -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 deleted file mode 100644 index d1b1777d..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/attendance/Types.java +++ /dev/null @@ -1,29 +0,0 @@ -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 deleted file mode 100644 index 466bfe4f..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/exams/Exam.java +++ /dev/null @@ -1,59 +0,0 @@ -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 deleted file mode 100644 index 8127e631..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/exams/ExamDay.java +++ /dev/null @@ -1,19 +0,0 @@ -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 deleted file mode 100644 index 391056ac..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.github.wulkanowy.api.exams; - -import org.apache.commons.lang3.StringUtils; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.api.generic.Week; - -import static io.github.wulkanowy.api.DateTimeUtilsKt.getDateAsTick; -import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; - -public class ExamsWeek { - - private static final String EXAMS_PAGE_URL = "Sprawdziany.mvc/Terminarz?rodzajWidoku=2&data="; - - private final SnP snp; - - public ExamsWeek(SnP snp) { - this.snp = snp; - } - - public Week getCurrent() throws IOException, VulcanException { - return getWeek("", true); - } - - public Week getWeek(String date, final boolean onlyNotEmpty) throws IOException, VulcanException { - Document examsPage = snp.getSnPPageDocument(EXAMS_PAGE_URL + getDateAsTick(date)); - 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) { - String[] dateHeader = dayHeading.text().split(", "); - day.setDayName(StringUtils.capitalize(dateHeader[0])); - day.setDate(getFormattedDate(dateHeader[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(getFormattedDate(snp.getRowDataChildValue(e, 4).split(", ")[1])) - ); - } - - days.add(day); - } - - - return new Week() - .setStartDayDate(getFormattedDate(examsPage.select(".mainContainer > 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 deleted file mode 100644 index fbb5ee0a..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Day.java +++ /dev/null @@ -1,44 +0,0 @@ -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 = ""; - - 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 Day setDayName(String dayName) { - this.dayName = dayName; - return this; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Diary.java b/api/src/main/java/io/github/wulkanowy/api/generic/Diary.java deleted file mode 100644 index 5c7c8593..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Diary.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.api.generic; - -public class Diary implements ParamItem { - - private String id = ""; - - private String name = ""; - - private boolean current = false; - - public String getId() { - return id; - } - - public Diary setId(String id) { - this.id = id; - return this; - } - - public String getName() { - return name; - } - - public Diary setName(String name) { - this.name = name; - return this; - } - - @Override - public boolean isCurrent() { - return current; - } - - public Diary setCurrent(boolean current) { - this.current = current; - return this; - } -} 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 deleted file mode 100644 index 27bf9bf0..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Lesson.java +++ /dev/null @@ -1,241 +0,0 @@ -package io.github.wulkanowy.api.generic; - -public class Lesson { - - private int number = 0; - - 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 int getNumber() { - return number; - } - - public Lesson setNumber(int 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 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 deleted file mode 100644 index e38ef3a3..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Month.java +++ /dev/null @@ -1,26 +0,0 @@ -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/ParamItem.java b/api/src/main/java/io/github/wulkanowy/api/generic/ParamItem.java deleted file mode 100644 index e7edfbf4..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/ParamItem.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.wulkanowy.api.generic; - -public interface ParamItem { - - ParamItem setId(String id); - - ParamItem setName(String name); - - ParamItem setCurrent(boolean isCurrent); - - boolean isCurrent(); -} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/School.kt b/api/src/main/java/io/github/wulkanowy/api/generic/School.kt deleted file mode 100644 index c14fc5d2..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/School.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.wulkanowy.api.generic - -data class School( - val name: String, - val id: String, - val current: Boolean -) diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Semester.java b/api/src/main/java/io/github/wulkanowy/api/generic/Semester.java deleted file mode 100644 index db4a724d..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Semester.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.api.generic; - -public class Semester implements ParamItem { - - private String id = ""; - - private String name = ""; - - private boolean current = false; - - public String getId() { - return id; - } - - public Semester setId(String id) { - this.id = id; - return this; - } - - public String getName() { - return name; - } - - public Semester setName(String number) { - this.name = number; - return this; - } - - public boolean isCurrent() { - return current; - } - - public Semester setCurrent(boolean current) { - this.current = current; - return this; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Student.java b/api/src/main/java/io/github/wulkanowy/api/generic/Student.java deleted file mode 100644 index 4ed5dd37..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Student.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.api.generic; - -public class Student implements ParamItem { - - private String id = ""; - - private String name = ""; - - private boolean current = false; - - public String getId() { - return id; - } - - public Student setId(String id) { - this.id = id; - return this; - } - - public String getName() { - return name; - } - - public Student setName(String name) { - this.name = name; - return this; - } - - public boolean isCurrent() { - return current; - } - - public Student setCurrent(boolean current) { - this.current = current; - 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 deleted file mode 100644 index fcf27bef..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Subject.java +++ /dev/null @@ -1,26 +0,0 @@ -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 deleted file mode 100644 index 1e46f394..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/generic/Week.java +++ /dev/null @@ -1,33 +0,0 @@ -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 deleted file mode 100644 index 31e1bbbd..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/grades/Grade.java +++ /dev/null @@ -1,100 +0,0 @@ -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 = ""; - - 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; - } -} 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 deleted file mode 100644 index cb9fe465..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java +++ /dev/null @@ -1,75 +0,0 @@ -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 java.util.regex.Matcher; -import java.util.regex.Pattern; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; - -import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; - -public class GradesList { - - private static final String GRADES_PAGE_URL = "Oceny/Wszystkie?details=2&okres="; - - private SnP snp; - - public GradesList(SnP snp) { - this.snp = snp; - } - - public List getAll(String semester) throws IOException, VulcanException { - Document gradesPage = snp.getSnPPageDocument(GRADES_PAGE_URL + semester); - Elements gradesRows = gradesPage.select(".ocenySzczegoly-table > tbody > tr"); - - List grades = new ArrayList<>(); - - for (Element row : gradesRows) { - if ("Brak ocen".equals(row.select("td:nth-child(2)").text())) { - continue; - } - - grades.add(getGrade(row)); - } - - return grades; - } - - private Grade getGrade(Element row) { - String descriptions = row.select("td:nth-child(3)").text(); - - String symbol = descriptions.split(", ")[0]; - String description = descriptions.replaceFirst(Pattern.quote(symbol), "").replaceFirst(", ", ""); - String color = getColor(row.select("td:nth-child(2) span.ocenaCzastkowa").attr("style")); - String date = getFormattedDate(row.select("td:nth-child(5)").text()); - - return 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(date) - .setTeacher(row.select("td:nth-child(6)").text()); - } - - private String getColor(String styleAttr) { - Pattern pattern = Pattern.compile("#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})"); - Matcher matcher = pattern.matcher(styleAttr); - - String color = ""; - while (matcher.find()) { - color = matcher.group(1); - } - - return color; - } -} 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 deleted file mode 100644 index c7917810..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/grades/Subject.java +++ /dev/null @@ -1,40 +0,0 @@ -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 deleted file mode 100644 index 37902d0d..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/grades/SubjectsList.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.wulkanowy.api.grades; - -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; - -public class SubjectsList { - - private static final String SUBJECTS_PAGE_URL = "Oceny/Wszystkie?details=1&okres="; - - private SnP snp; - - public SubjectsList(SnP snp) { - this.snp = snp; - } - - - public List getAll() throws IOException, VulcanException { - return getAll(""); - } - - public List getAll(String semester) throws IOException, VulcanException { - Document subjectPage = snp.getSnPPageDocument(SUBJECTS_PAGE_URL + semester); - - 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 deleted file mode 100644 index de3901ff..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/login/AccountPermissionException.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.api.login; - -import io.github.wulkanowy.api.VulcanException; - -public class AccountPermissionException extends VulcanException { - - AccountPermissionException(String message) { - super(message); - } -} 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 deleted file mode 100644 index 13efd4a1..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/login/BadCredentialsException.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.api.login; - -import io.github.wulkanowy.api.VulcanException; - -public class BadCredentialsException extends VulcanException { - - BadCredentialsException(String message) { - super(message); - } -} 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 deleted file mode 100644 index 443a9387..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/login/Login.java +++ /dev/null @@ -1,146 +0,0 @@ -package io.github.wulkanowy.api.login; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.parser.Parser; -import org.jsoup.select.Elements; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; - -import io.github.wulkanowy.api.Client; -import io.github.wulkanowy.api.NotLoggedInErrorException; -import io.github.wulkanowy.api.VulcanException; - -public class Login { - - protected static final String LOGIN_PAGE_URL = "{schema}://cufs.{host}/{symbol}/Account/LogOn"; - - private static final String LOGIN_PAGE_URL_QUERY = "?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 Client client; - - private static final Logger logger = LoggerFactory.getLogger(Login.class); - - public Login(Client client) { - this.client = client; - } - - public void login(String email, String password, String symbol) throws VulcanException, IOException { - Document certDoc = sendCredentials(email, password); - - if ("Błąd".equals(certDoc.title())) { - client.clearCookies(); - throw new NotLoggedInErrorException(certDoc.body().text()); - } - - sendCertificate(certDoc, symbol); - } - - Document sendCredentials(String email, String password) throws IOException, VulcanException { - String[][] credentials = new String[][]{ - {"LoginName", email}, - {"Password", password} - }; - - Document nextDoc = sendCredentialsData(credentials, LOGIN_PAGE_URL + LOGIN_PAGE_URL_QUERY.replace(":", "%253A")); - - Element errorMessage = nextDoc.selectFirst(".ErrorMessage, #ErrorTextLabel"); - if (null != errorMessage) { - throw new BadCredentialsException(errorMessage.text()); - } - - return nextDoc; - } - - private Document sendCredentialsData(String[][] credentials, String nextUrl) throws IOException, VulcanException { - Element formFirst = client.getPageByUrl(nextUrl, false).selectFirst("#form1"); - - if (null != formFirst) { // only on adfs login - Document formSecond = client.postPageByUrl( - formFirst.attr("abs:action"), - getFormStateParams(formFirst, "", "") - ); - credentials = getFormStateParams(formSecond, credentials[0][1], credentials[1][1]); - nextUrl = formSecond.selectFirst("#form1").attr("abs:action"); - } - - return client.postPageByUrl(nextUrl, credentials); - } - - private String[][] getFormStateParams(Element form, String email, String password) { - return new String[][]{ - {"__VIEWSTATE", form.select("#__VIEWSTATE").val()}, - {"__VIEWSTATEGENERATOR", form.select("#__VIEWSTATEGENERATOR").val()}, - {"__EVENTVALIDATION", form.select("#__EVENTVALIDATION").val()}, - {"__db", form.select("input[name=__db]").val()}, - {"PassiveSignInButton.x", "0"}, - {"PassiveSignInButton.y", "0"}, - {"SubmitButton.x", "0"}, - {"SubmitButton.y", "0"}, - {"UsernameTextBox", email}, - {"PasswordTextBox", password}, - }; - } - - void sendCertificate(Document doc, String defaultSymbol) throws IOException, VulcanException { - client.setSymbol(findSymbol(defaultSymbol, doc.select("input[name=wresult]").val())); - - Document targetDoc = sendCertData(doc); - String title = targetDoc.title(); - - if ("Working...".equals(title)) { // on adfs login - logger.info("ADFS login"); - title = sendCertData(targetDoc).title(); - } - - if ("Logowanie".equals(title)) { - throw new AccountPermissionException("No account access. Try another symbol"); - } - - if (!"Uonet+".equals(title)) { - logger.debug("Login failed. Body: {}", targetDoc.body()); - throw new LoginErrorException("Expected page title `UONET+`, got " + title); - } - - client.setSchools(new StartPage(client).getSchools(targetDoc)); - } - - private Document sendCertData(Document doc) throws IOException, VulcanException { - String url = doc.select("form[name=hiddenform]").attr("action"); - - return client.postPageByUrl(url.replaceFirst("Default", "{symbol}"), new String[][]{ - {"wa", "wsignin1.0"}, - {"wresult", doc.select("input[name=wresult]").val()}, - {"wctx", doc.select("input[name=wctx]").val()} - }); - } - - private String findSymbol(String symbol, String certificate) throws AccountPermissionException { - if ("Default".equals(symbol)) { - return findSymbolInCertificate(certificate); - } - - return symbol; - } - - String findSymbolInCertificate(String certificate) throws AccountPermissionException { - Elements instances = Jsoup - .parse(certificate.replaceAll(":", ""), "", Parser.xmlParser()) - .select("[AttributeName=\"UserInstance\"] samlAttributeValue"); - - if (instances.isEmpty()) { // on adfs login - return ""; - } - - if (instances.size() < 2) { // 1st index is always `Default` - throw new AccountPermissionException("First login detected, specify symbol"); - } - - return instances.get(1).text(); - } -} 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 deleted file mode 100644 index be7439df..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/login/LoginErrorException.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.api.login; - -import io.github.wulkanowy.api.NotLoggedInErrorException; - -class LoginErrorException extends NotLoggedInErrorException { - - LoginErrorException(String message) { - super(message); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/login/StartPage.kt b/api/src/main/java/io/github/wulkanowy/api/login/StartPage.kt deleted file mode 100644 index 2b8a3700..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/login/StartPage.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.wulkanowy.api.login - -import io.github.wulkanowy.api.Client -import io.github.wulkanowy.api.VulcanException -import io.github.wulkanowy.api.generic.School -import org.jsoup.nodes.Document -import org.slf4j.LoggerFactory - -class StartPage(val client: Client) { - - private val logger = LoggerFactory.getLogger(StartPage::class.java) - - fun getSchools(startPage: Document): MutableList { - val schoolList = mutableListOf() - - val snpLinks = startPage.select(".panel.linkownia.pracownik.klient .appLink a") - - logger.debug("SnP links: {}", snpLinks.size) - - if (snpLinks.isEmpty()) { - throw VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?") - } - - snpLinks.map { - schoolList.add(School( - it.text(), - getExtractedIdFromUrl(it.attr("href")), - it == snpLinks.first() - )) - } - - return schoolList - } - - internal fun getExtractedIdFromUrl(snpPageUrl: String): String { - val path = snpPageUrl.split(client.host).getOrNull(1)?.split("/") - - if (6 != path?.size) { - logger.error("Expected snp url, got {}", snpPageUrl) - throw VulcanException("Na pewno używasz konta z dostępem do Witryny ucznia i rodzica?") - } - - return path[2] - } -} 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 deleted file mode 100644 index 14dca67b..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/messages/BadRequestException.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.api.messages; - -import io.github.wulkanowy.api.VulcanException; - -class BadRequestException extends VulcanException { - - BadRequestException(String message) { - super(message); - } -} 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 deleted file mode 100644 index 331ec819..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/messages/Message.java +++ /dev/null @@ -1,30 +0,0 @@ -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 deleted file mode 100644 index ec2a3fba..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/messages/Messages.java +++ /dev/null @@ -1,99 +0,0 @@ -package io.github.wulkanowy.api.messages; - -import com.google.gson.Gson; -import com.google.gson.JsonParseException; - -import java.io.IOException; -import java.util.List; - -import io.github.wulkanowy.api.Client; -import io.github.wulkanowy.api.NotLoggedInErrorException; -import io.github.wulkanowy.api.VulcanException; - -public class Messages { - - private static final String BASE_URL = "{schema}://uonetplus-uzytkownik.{host}/{symbol}/"; - - private static final String LIST_BASE_URL = BASE_URL + "Wiadomosc.mvc/"; - - private static final String RECEIVED_URL = LIST_BASE_URL + "GetWiadomosciOdebrane"; - - private static final String SENT_URL = LIST_BASE_URL + "GetWiadomosciWyslane"; - - private static final String DELETED_URL = LIST_BASE_URL + "GetWiadomosciUsuniete"; - - private static final String MESSAGE_URL = LIST_BASE_URL + "GetTrescWiadomosci"; - - public static final int RECEIVED_FOLDER = 1; - - public static final int SENT_FOLDER = 2; - - public static final int DELETED_FOLDER = 3; - - private static final String ERROR_TITLE = "Błąd strony"; - - private Client client; - - public Messages(Client client) { - this.client = client; - } - - public List getReceived() throws IOException, VulcanException { - return getMessages(RECEIVED_URL); - } - - public List getSent() throws IOException, VulcanException { - return getMessages(SENT_URL); - } - - public List getDeleted() throws IOException, VulcanException { - return getMessages(DELETED_URL); - } - - private List getMessages(String url) throws IOException, VulcanException { - String res = client.getJsonStringByUrl(url); - - List messages; - - try { - messages = new Gson().fromJson(res, MessagesContainer.class).data; - } catch (JsonParseException e) { - if (res.contains(ERROR_TITLE)) { - throw new BadRequestException(ERROR_TITLE); - } - - throw new NotLoggedInErrorException("You are probably not logged in"); - } - - return messages; - } - - public Message getMessage(int id, int folder) throws IOException, VulcanException { - String res = client.postJsonStringByUrl(MESSAGE_URL, new String[][]{ - {"idWiadomosc", String.valueOf(id)}, - {"Folder", String.valueOf(folder)} - }); - - Message message; - - try { - message = new Gson().fromJson(res, MessageContainer.class).data; - } catch (JsonParseException e) { - if (res.contains(ERROR_TITLE)) { - throw new BadRequestException(ERROR_TITLE); - } - - throw new NotLoggedInErrorException("You are probably not logged in. Force login"); - } - - 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/mobile/RegisterDevice.kt b/api/src/main/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt deleted file mode 100644 index f0da01e9..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.api.mobile - -import io.github.wulkanowy.api.SnP -import org.jsoup.nodes.Element - -class RegisterDevice(private val snp: SnP) { - - companion object { - const val REGISTER_URL = "DostepMobilny.mvc/Rejestruj" - } - - data class Token( - val token: String, - val symbol: String, - val pin: String - ) - - fun getToken(): Token { - val form = snp.getSnPPageDocument(REGISTER_URL).selectFirst("#rejestracja-formularz") - - val fields = form.select(".blockElement") - - return Token( - getValue(fields[1]), - getValue(fields[2]), - getValue(fields[3]) - ) - } - - fun getValue(e: Element): String { - return e.text().split(":")[1].trim() - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/mobile/RegisteredDevices.kt b/api/src/main/java/io/github/wulkanowy/api/mobile/RegisteredDevices.kt deleted file mode 100644 index d622b4de..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/mobile/RegisteredDevices.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.api.mobile - -import io.github.wulkanowy.api.SnP -import io.github.wulkanowy.api.getFormattedDate - -class RegisteredDevices(private val snp: SnP) { - - companion object { - const val DEVICES_LIST_URL = "DostepMobilny.mvc" - } - - data class Device( - val name: String, - val system: String, - val date: String, - val id: Int - ) - - fun getList(): List { - val items = snp.getSnPPageDocument(DEVICES_LIST_URL).select("table tbody tr") - val devices: MutableList = mutableListOf() - - for (item in items) { - val cells = item.select("td") - val system = cells[0].text().split("(").last().removeSuffix(")") - - devices.add(Device( - cells[0].text().replace(" ($system)", ""), - system, - getFormattedDate(cells[1].text(), "dd.MM.yyyy 'godz:' HH:mm:ss", "yyyy-MM-dd HH:mm:ss"), - cells[2].select("a").attr("href") - .split("/").last().toInt() - )) - } - - return devices - } -} 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 deleted file mode 100644 index 346df832..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.api.notes; - -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; - -public class AchievementsList { - - private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie"; - - private SnP snp; - - public AchievementsList(SnP snp) { - this.snp = snp; - } - - public List getAllAchievements() throws IOException, VulcanException { - Element pageFragment = snp.getSnPPageDocument(NOTES_PAGE_URL) - .select(".mainContainer > div").get(1); - Elements items = pageFragment.select("article"); - - List achievements = new ArrayList<>(); - - 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 deleted file mode 100644 index b1fa68b9..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/notes/Note.java +++ /dev/null @@ -1,48 +0,0 @@ -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 deleted file mode 100644 index 54f57edf..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.wulkanowy.api.notes; - -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; - -import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; - -public class NotesList { - - private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie"; - - private SnP snp; - - public NotesList(SnP snp) { - this.snp = snp; - } - - public List getAllNotes() throws IOException, VulcanException { - Element pageFragment = snp.getSnPPageDocument(NOTES_PAGE_URL) - .select(".mainContainer > div").get(0); - Elements items = pageFragment.select("article"); - Elements dates = pageFragment.select("h2"); - - List notes = new ArrayList<>(); - - int index = 0; - for (Element item : items) { - notes.add(new Note() - .setDate(getFormattedDate(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 deleted file mode 100644 index f135163a..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/school/SchoolData.java +++ /dev/null @@ -1,59 +0,0 @@ -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 deleted file mode 100644 index f0dcd43d..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.api.school; - -import org.jsoup.nodes.Element; - -import java.io.IOException; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; - -public class SchoolInfo { - - private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele"; - - private SnP snp; - - public SchoolInfo(SnP snp) { - this.snp = snp; - } - - public SchoolData getSchoolData() throws IOException, VulcanException { - Element e = snp.getSnPPageDocument(SCHOOL_PAGE_URL) - .select(".mainContainer > article").get(0); - - return new SchoolData() - .setName(snp.getRowDataChildValue(e, 1)) - .setAddress(snp.getRowDataChildValue(e, 2)) - .setPhoneNumber(snp.getRowDataChildValue(e, 3)) - .setHeadmaster(snp.getRowDataChildValue(e, 4)) - .setPedagogue(snp.getRowDataChildValue(e, 5).split(", ")); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/school/Subject.java b/api/src/main/java/io/github/wulkanowy/api/school/Subject.java deleted file mode 100644 index 0a3c0957..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/school/Subject.java +++ /dev/null @@ -1,26 +0,0 @@ -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 deleted file mode 100644 index 7d41162e..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/school/TeachersData.java +++ /dev/null @@ -1,39 +0,0 @@ -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 deleted file mode 100644 index ec8429bd..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.wulkanowy.api.school; - -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; - -public class TeachersInfo { - - private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele"; - - private SnP snp; - - public TeachersInfo(SnP snp) { - this.snp = snp; - } - - public TeachersData getTeachersData() throws IOException, VulcanException { - Document doc = snp.getSnPPageDocument(SCHOOL_PAGE_URL); - Elements rows = doc.select(".mainContainer > table tbody tr"); - String description = doc.select(".mainContainer > p").first().text(); - - List subjects = new ArrayList<>(); - - for (Element subject : rows) { - subjects.add(new Subject() - .setName(subject.select("td").get(1).text()) - .setTeachers(subject.select("td").get(2).text().split(", ")) - ); - } - - return new TeachersData() - .setClassName(description.split(", ")[0].split(": ")[1].trim()) - .setClassTeacher(description.split("Wychowawcy:")[1].trim().split(", ")) - .setSubjects(subjects); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/timetable/LessonTypes.java b/api/src/main/java/io/github/wulkanowy/api/timetable/LessonTypes.java deleted file mode 100644 index be1f44e9..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/timetable/LessonTypes.java +++ /dev/null @@ -1,16 +0,0 @@ -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 deleted file mode 100644 index 95c40167..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java +++ /dev/null @@ -1,243 +0,0 @@ -package io.github.wulkanowy.api.timetable; - -import org.apache.commons.lang3.StringUtils; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.api.generic.Lesson; -import io.github.wulkanowy.api.generic.Week; - -import static io.github.wulkanowy.api.DateTimeUtilsKt.getDateAsTick; -import static io.github.wulkanowy.api.DateTimeUtilsKt.getFormattedDate; - -public class Timetable { - - private static final String TIMETABLE_PAGE_URL = "Lekcja.mvc/PlanZajec?data="; - - private SnP snp; - - private static final Logger logger = LoggerFactory.getLogger(Timetable.class); - - public Timetable(SnP snp) { - this.snp = snp; - } - - public Week getWeekTable() throws IOException, VulcanException { - return getWeekTable(""); - } - - public Week getWeekTable(final String date) throws IOException, VulcanException { - Element table = snp.getSnPPageDocument(TIMETABLE_PAGE_URL + getDateAsTick(date)) - .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) { - List days = new ArrayList<>(); - int numberOfDays = tableHeaderCells.size(); - - if (numberOfDays > 7) { - logger.info("Number of days: {}", numberOfDays); - } - - for (int i = 2; i < numberOfDays; i++) { - String[] dayHeaderCell = tableHeaderCells.get(i).html().split("
"); - - TimetableDay day = new TimetableDay(); - day.setDayName(dayHeaderCell[0]); - day.setDate(getFormattedDate(dayHeaderCell[1].trim())); - - 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(Integer.valueOf(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: - Element span = e.last().selectFirst("span"); - if (null == span) { - addLessonInfoFromElement(lesson, e.first()); - } else if (span.hasClass(LessonTypes.CLASS_MOVED_OR_CANCELED)) { - lesson.setNewMovedInOrChanged(true); - lesson.setDescription("poprzednio: " + getLessonAndGroupInfoFromSpan(span)[0]); - addLessonInfoFromElement(lesson, e.first()); - } else { - addLessonInfoFromElement(lesson, e.last()); - } - break; - case 3: - addLessonInfoFromElement(lesson, e.get(1)); - break; - default: - lesson.setEmpty(true); - break; - } - } - - private void moveWarningToLessonNode(Elements e) { - Elements warn = e.select(".uwaga-panel"); - - if (!warn.isEmpty()) { - e.select("span").last() - .addClass("x-treelabel-rlz") - .text(warn.text()); - e.remove(1); - } - } - - private void addLessonInfoFromElement(Lesson lesson, Element e) { - Elements spans = e.select("span"); - - if (spans.isEmpty()) { - logger.warn("Lesson span is empty"); - return; - } - - 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.defaultString(StringUtils.substringBetween( - spans.last().text(), "(", ")"), spans.last().text()) - + " (poprzednio: " + spans.get(0).text() + ")"); - } else if (9 == spans.size()) { - String[] subjectAndGroupInfo = getLessonAndGroupInfoFromSpan(spans.get(4)); - 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.defaultString(StringUtils.substringBetween( - spans.last().text(), "(", ")"), spans.last().text()) - + " (poprzednio: " + getLessonAndGroupInfoFromSpan(spans.get(0))[0] + ")"); - } else if (4 <= spans.size()) { - lesson.setSubject(spans.get(0).text()); - lesson.setTeacher(spans.get(1).text()); - lesson.setRoom(spans.get(2).text()); - lesson.setDescription(StringUtils.defaultString(StringUtils.substringBetween( - spans.last().text(), "(", ")"), 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) { - if (!span.text().contains("[")) { - return new String[] {span.text(), ""}; - } - - String[] subjectNameArray = span.text().split(" "); - String groupName = subjectNameArray[subjectNameArray.length - 1]; - - return new String[]{ - span.text().replace(" " + groupName, ""), - StringUtils.defaultString(StringUtils.substringBetween( - groupName, "[", "]"), groupName) - }; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/timetable/TimetableDay.java b/api/src/main/java/io/github/wulkanowy/api/timetable/TimetableDay.java deleted file mode 100644 index 1aa29de2..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/timetable/TimetableDay.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.api.timetable; - -import io.github.wulkanowy.api.generic.Day; - -public class TimetableDay extends Day { - - private boolean isFreeDay = false; - - private String freeDayName = ""; - - 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/user/AddressData.java b/api/src/main/java/io/github/wulkanowy/api/user/AddressData.java deleted file mode 100644 index 3d6fc6d5..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/user/AddressData.java +++ /dev/null @@ -1,40 +0,0 @@ -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 deleted file mode 100644 index 4edc5117..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/user/BasicInformation.java +++ /dev/null @@ -1,70 +0,0 @@ -package io.github.wulkanowy.api.user; - -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; - -import java.io.IOException; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; - -public class BasicInformation { - - private static final String STUDENT_DATA_PAGE_URL = "Uczen.mvc/DanePodstawowe"; - - private static final String CONTENT_QUERY = ".mainContainer > article"; - - private Document studentDataPageDocument; - - private SnP snp; - - public BasicInformation(SnP snp) { - this.snp = snp; - } - - public Document getStudentDataPageDocument() throws IOException, VulcanException { - if (null == studentDataPageDocument) { - studentDataPageDocument = snp.getSnPPageDocument(STUDENT_DATA_PAGE_URL); - } - - return studentDataPageDocument; - } - - public PersonalData getPersonalData() throws IOException, VulcanException { - Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(0); - - String name = snp.getRowDataChildValue(e, 1); - String[] names = name.split(" "); - - return new PersonalData() - .setName(name) - .setFirstName(names[0]) - .setSurname(names[names.length - 1]) - .setFirstAndLastName(names[0] + " " + names[names.length - 1]) - .setDateAndBirthPlace(snp.getRowDataChildValue(e, 2)) - .setPesel(snp.getRowDataChildValue(e, 3)) - .setGender(snp.getRowDataChildValue(e, 4)) - .setPolishCitizenship("Tak".equals(snp.getRowDataChildValue(e, 5))) - .setFamilyName(snp.getRowDataChildValue(e, 6)) - .setParentsNames(snp.getRowDataChildValue(e, 7)); - } - - public AddressData getAddressData() throws IOException, VulcanException { - Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(1); - - return new AddressData() - .setAddress(snp.getRowDataChildValue(e, 1)) - .setRegisteredAddress(snp.getRowDataChildValue(e, 2)) - .setCorrespondenceAddress(snp.getRowDataChildValue(e, 3)); - - } - - public ContactDetails getContactDetails() throws IOException, VulcanException { - Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(2); - - return new ContactDetails() - .setPhoneNumber(snp.getRowDataChildValue(e, 1)) - .setCellPhoneNumber(snp.getRowDataChildValue(e, 2)) - .setEmail(snp.getRowDataChildValue(e, 3)); - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/ContactDetails.java b/api/src/main/java/io/github/wulkanowy/api/user/ContactDetails.java deleted file mode 100644 index c8556944..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/user/ContactDetails.java +++ /dev/null @@ -1,37 +0,0 @@ -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 deleted file mode 100644 index f5a459ee..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/user/FamilyInformation.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.api.user; - -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.api.SnP; -import io.github.wulkanowy.api.VulcanException; - -public class FamilyInformation { - - private static final String STUDENT_DATA_PAGE_URL = "Uczen.mvc/DanePodstawowe"; - - private SnP snp; - - public FamilyInformation(SnP snp) { - this.snp = snp; - } - - public List getFamilyMembers() throws IOException, VulcanException { - Elements membersElements = snp.getSnPPageDocument(STUDENT_DATA_PAGE_URL) - .select(".mainContainer > article:nth-of-type(n+4)"); - - List familyMembers = new ArrayList<>(); - - for (Element e : membersElements) { - familyMembers.add(new FamilyMember() - .setName(snp.getRowDataChildValue(e, 1)) - .setKinship(snp.getRowDataChildValue(e, 2)) - .setAddress(snp.getRowDataChildValue(e, 3)) - .setTelephones(snp.getRowDataChildValue(e, 4)) - .setEmail(snp.getRowDataChildValue(e, 5)) - ); - } - - return familyMembers; - } -} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/FamilyMember.java b/api/src/main/java/io/github/wulkanowy/api/user/FamilyMember.java deleted file mode 100644 index dfb96674..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/user/FamilyMember.java +++ /dev/null @@ -1,59 +0,0 @@ -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 deleted file mode 100644 index d2991e61..00000000 --- a/api/src/main/java/io/github/wulkanowy/api/user/PersonalData.java +++ /dev/null @@ -1,114 +0,0 @@ -package io.github.wulkanowy.api.user; - -public class PersonalData { - - private String name = ""; - - private String firstName = ""; - - private String surname = ""; - - private String firstAndLastName = ""; - - private String dateAndBirthPlace = ""; - - private String pesel = ""; - - private String gender = ""; - - private boolean isPolishCitizenship; - - private String familyName = ""; - - private String parentsNames = ""; - - public String getName() { - return name; - } - - public PersonalData setName(String name) { - this.name = name; - return this; - } - - public String getFirstName() { - return firstName; - } - - public PersonalData setFirstName(String firstName) { - this.firstName = firstName; - return this; - } - - public String getSurname() { - return surname; - } - - public PersonalData setSurname(String surname) { - this.surname = surname; - return this; - } - - public String getFirstAndLastName() { - return firstAndLastName; - } - - public PersonalData setFirstAndLastName(String firstAndLastName) { - this.firstAndLastName = firstAndLastName; - return this; - } - - public String getDateAndBirthPlace() { - return dateAndBirthPlace; - } - - public PersonalData setDateAndBirthPlace(String dateAndBirthPlace) { - this.dateAndBirthPlace = dateAndBirthPlace; - return this; - } - - public String getPesel() { - return pesel; - } - - public PersonalData setPesel(String pesel) { - this.pesel = pesel; - return this; - } - - public String getGender() { - return gender; - } - - public PersonalData setGender(String gender) { - this.gender = gender; - return this; - } - - public boolean isPolishCitizenship() { - return isPolishCitizenship; - } - - public PersonalData setPolishCitizenship(boolean polishCitizenship) { - isPolishCitizenship = polishCitizenship; - return this; - } - - public String getFamilyName() { - return familyName; - } - - public PersonalData setFamilyName(String familyName) { - this.familyName = familyName; - return this; - } - - public String getParentsNames() { - return parentsNames; - } - - public PersonalData setParentsNames(String parentsNames) { - this.parentsNames = parentsNames; - return this; - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/ClientTest.java b/api/src/test/java/io/github/wulkanowy/api/ClientTest.java deleted file mode 100644 index 298affac..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/ClientTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package io.github.wulkanowy.api; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.junit.Assert; -import org.junit.Test; - -public class ClientTest { - - private String getFixtureAsString(String fixtureFileName) { - return FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName)); - } - - @Test - public void setFullEndpointInfoTest() { - Client client = new Client("http://fakelog.cf\\\\admin", "pass", "Default", "123"); - - Assert.assertEquals("fakelog.cf", client.getHost()); - Assert.assertEquals("Default", client.getSymbol()); - } - - @Test - public void setFullEndpointInfoWithSymbolTest() { - Client client = new Client("http://fakelog.cf/notdefault\\\\admin", "pass", "Default", "123"); - - Assert.assertEquals("fakelog.cf", client.getHost()); - Assert.assertEquals("notdefault", client.getSymbol()); // - } - - @Test - public void checkForNoErrorsTest() throws Exception { - Client client = new Client("", "", "", "123"); - - Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-success.html")); - - Assert.assertEquals(doc, client.checkForErrors(doc, 200)); - } - - @Test(expected = VulcanOfflineException.class) - public void checkForErrorsOffline() throws Exception { - Client client = new Client("", "", "", "123"); - - Document doc = Jsoup.parse(getFixtureAsString("login/PrzerwaTechniczna.html")); - - client.checkForErrors(doc, 200); - } - - @Test(expected = NotLoggedInErrorException.class) - public void checkForErrors() throws Exception { - Client client = new Client("", "", "", "123"); - - Document doc = Jsoup.parse(getFixtureAsString("login/Logowanie-notLoggedIn.html")); - - client.checkForErrors(doc, 200); - } - - @Test - public void getFilledUrlTest() throws Exception { - Client client = new Client("http://fakelog.cf\\\\admin", "", "symbol123", "321"); - - Assert.assertEquals("http://uonetplus-opiekun.fakelog.cf/symbol123/321/Oceny/Wszystkie", - client.getFilledUrl("{schema}://uonetplus-opiekun.{host}/{symbol}/{ID}/Oceny/Wszystkie")); - } - - @Test - public void getSymbolTest() { - Client client = new Client("", "", "symbol4321", "123"); - - Assert.assertEquals("symbol4321", client.getSymbol()); - } - - @Test - public void getSchoolIdTest() throws Exception { - Client client = new Client("", "", "1", "123456"); - - Assert.assertEquals("123456", client.getSchoolId()); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/DateTimeUtilsTest.kt b/api/src/test/java/io/github/wulkanowy/api/DateTimeUtilsTest.kt deleted file mode 100644 index 1105b2a7..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/DateTimeUtilsTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.api - -import org.junit.Assert -import org.junit.Test -import java.text.ParseException -import java.text.SimpleDateFormat -import java.util.* - -class DateTimeUtilsTest { - - @Test - fun getTicksDateObjectTest() { - val format = SimpleDateFormat("dd.MM.yyyy", Locale.ROOT) - format.timeZone = TimeZone.getTimeZone("UTC") - val date = format.parse("31.07.2017") - - Assert.assertEquals(636370560000000000L, getDateAsTick(date)) - - val calendar = Calendar.getInstance() - calendar.time = date - calendar.add(Calendar.DAY_OF_YEAR, -14) - val dateTwoWeekBefore = calendar.time - - Assert.assertEquals(636358464000000000L, getDateAsTick(dateTwoWeekBefore)) - } - - @Test(expected = ParseException::class) - fun getTicsStringInvalidFormatTest() { - Assert.assertEquals(636370560000000000L, getDateAsTick("31.07.2017", "dd.MMM.yyyy")) - } - - @Test - fun getTicsStringFormatTest() { - Assert.assertEquals(636370560000000000L, getDateAsTick("31.07.2017", "dd.MM.yyyy")) - } - - @Test - fun getTicsStringTest() { - Assert.assertEquals("636370560000000000", getDateAsTick("2017-07-31")) - Assert.assertEquals("636334272000000000", getDateAsTick("2017-06-19")) - Assert.assertEquals("636189120000000000", getDateAsTick("2017-01-02")) - Assert.assertEquals("636080256000000000", getDateAsTick("2016-08-29")) - } - - @Test - fun getDateTest() { - val format = SimpleDateFormat("dd.MM.yyyy", Locale.ROOT) - format.timeZone = TimeZone.getTimeZone("UTC") - val date = format.parse("31.07.2017") - - Assert.assertEquals(date, getDate(636370560000000000L)) - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/FixtureHelper.java b/api/src/test/java/io/github/wulkanowy/api/FixtureHelper.java deleted file mode 100644 index 94dd47b8..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/FixtureHelper.java +++ /dev/null @@ -1,12 +0,0 @@ -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 deleted file mode 100644 index 4c6443b1..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java +++ /dev/null @@ -1,103 +0,0 @@ -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.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.api.generic.Semester; - -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); - Mockito.when(client.getPageByUrl( - Mockito.anyString(), - Mockito.anyBoolean(), Mockito.anyMap())).thenReturn(gradesPageDocument); - } - - @Test - public void snpTest() { - StudentAndParent snp = new StudentAndParent(client, "1234", null); - Assert.assertEquals("1234", snp.getStudentID()); - } - - @Test - public void getSemestersTest() throws Exception { - SnP snp = new StudentAndParent(client, null, null); - List semesters = snp.getSemesters(); - - Assert.assertEquals(2, semesters.size()); - - Assert.assertEquals("1", semesters.get(0).getName()); - Assert.assertEquals("1234", semesters.get(0).getId()); - Assert.assertFalse(semesters.get(0).isCurrent()); - - Assert.assertEquals("2", semesters.get(1).getName()); - Assert.assertEquals("1235", semesters.get(1).getId()); - Assert.assertTrue(semesters.get(1).isCurrent()); - } - - @Test - public void getCurrentSemesterTest() { - List semesters = new ArrayList<>(); - semesters.add(new Semester().setName("1500100900").setId("1").setCurrent(false)); - semesters.add(new Semester().setName("1500100901").setId("2").setCurrent(true)); - - SnP snp = new StudentAndParent(client, null, null); - Semester semester = snp.getCurrent(semesters); - - Assert.assertTrue(semester.isCurrent()); - Assert.assertEquals("2", semester.getId()); - Assert.assertEquals("1500100901", semester.getName()); - } - - @Test - public void getCurrentSemesterFromEmptyTest() { - SnP snp = new StudentAndParent(client, null, null); - List semesters = new ArrayList<>(); - - Assert.assertNull(snp.getCurrent(semesters)); - } - - @Test - public void getDiariesAndStudentTest() throws IOException, VulcanException { - String input = FixtureHelper.getAsString(getClass().getResourceAsStream("WitrynaUczniaIRodzica.html")); - Document snpHome = Jsoup.parse(input); - - client = Mockito.mock(Client.class); - Mockito.when(client.getPageByUrl(Mockito.anyString())).thenReturn(snpHome); - SnP snp = new StudentAndParent(client, null, null); - - snp.setUp(); - - Assert.assertEquals("3Ti 2017", snp.getDiaries().get(0).getName()); - Assert.assertEquals("2Ti 2016", snp.getDiaries().get(1).getName()); - Assert.assertEquals("1Ti 2015", snp.getDiaries().get(2).getName()); - - Assert.assertEquals("1300", snp.getDiaries().get(0).getId()); - Assert.assertEquals("1200", snp.getDiaries().get(1).getId()); - Assert.assertEquals("1100", snp.getDiaries().get(2).getId()); - - Assert.assertTrue(snp.getDiaries().get(0).isCurrent()); - Assert.assertFalse(snp.getDiaries().get(1).isCurrent()); - Assert.assertFalse(snp.getDiaries().get(2).isCurrent()); - - Assert.assertEquals("Jan Kowal", snp.getStudents().get(0).getName()); - Assert.assertEquals("100", snp.getStudents().get(0).getId()); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java deleted file mode 100644 index 764e8009..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.api; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.mockito.Mockito; - -import io.github.wulkanowy.api.generic.Semester; - -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.getCurrent(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 deleted file mode 100644 index 287935c0..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.api; - -import org.hamcrest.CoreMatchers; -import org.junit.Assert; -import org.junit.Test; - -public class VulcanTest { - - @Test(expected = NotLoggedInErrorException.class) - public void getClientWithoutLoginTest() throws Exception { - Vulcan vulcan = new Vulcan(); - - vulcan.getClient(); - } - - @Test - public void getClientTest() throws Exception { - Vulcan vulcan = new Vulcan(); - vulcan.setCredentials("email", "password", "symbol", null, null, null); - - Assert.assertThat(vulcan.getClient(), CoreMatchers.instanceOf(Client.class)); - } - - @Test - public void getClientTwiceTest() throws Exception { - Vulcan vulcan = new Vulcan(); - vulcan.setCredentials("email", "password", "symbol", null, null, null); - - Assert.assertEquals(vulcan.getClient(), vulcan.getClient()); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceStatisticsTest.java b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceStatisticsTest.java deleted file mode 100644 index ca50f378..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceStatisticsTest.java +++ /dev/null @@ -1,144 +0,0 @@ -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 deleted file mode 100644 index 40ff4f06..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java +++ /dev/null @@ -1,165 +0,0 @@ -package io.github.wulkanowy.api.attendance; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.github.wulkanowy.api.StudentAndParentTestCase; - -public class AttendanceTableTest extends StudentAndParentTestCase { - - private AttendanceTable excellent; - - private AttendanceTable full; - - @Before - public void setUp() throws Exception { - excellent = new AttendanceTable(getSnp("Frekwencja-excellent.html")); - full = new AttendanceTable(getSnp("Frekwencja-full.html")); - } - - @Test - public void getWeekStartByDate() throws Exception { - Assert.assertEquals("2015-08-31", excellent.getWeekTable().getStartDayDate()); - Assert.assertEquals("2016-09-05", full.getWeekTable().getStartDayDate()); - } - - @Test - public void getWeekDaysNumber() throws Exception { - Assert.assertEquals(5, excellent.getWeekTable().getDays().size()); - Assert.assertEquals(5, full.getWeekTable().getDays().size()); - } - - @Test - public void getDayLessonsNumber() throws Exception { - Assert.assertEquals(14, excellent.getWeekTable().getDay(0).getLessons().size()); - Assert.assertEquals(14, full.getWeekTable().getDay(0).getLessons().size()); - } - - @Test - public void getDayDate() throws Exception { - Assert.assertEquals("2015-08-31", excellent.getWeekTable().getDay(0).getDate()); - Assert.assertEquals("2015-09-02", excellent.getWeekTable().getDay(2).getDate()); - Assert.assertEquals("2015-09-04", excellent.getWeekTable().getDay(4).getDate()); - - Assert.assertEquals("2016-09-05", full.getWeekTable().getDay(0).getDate()); - Assert.assertEquals("2016-09-07", full.getWeekTable().getDay(2).getDate()); - Assert.assertEquals("2016-09-09", full.getWeekTable().getDay(4).getDate()); - } - - @Test - public void getLessonSubject() throws Exception { - Assert.assertEquals("", - excellent.getWeekTable().getDay(0).getLesson(7).getSubject()); - Assert.assertEquals("Uroczyste rozpoczęcie roku szkolnego 2015/2016", - excellent.getWeekTable().getDay(1).getLesson(1).getSubject()); - Assert.assertEquals("Geografia", - excellent.getWeekTable().getDay(3).getLesson(4).getSubject()); - - Assert.assertEquals("Naprawa komputera", - full.getWeekTable().getDay(1).getLesson(8).getSubject()); - Assert.assertEquals("Religia", - full.getWeekTable().getDay(3).getLesson(1).getSubject()); - Assert.assertEquals("Metodologia programowania", - full.getWeekTable().getDay(4).getLesson(5).getSubject()); - } - - @Test - public void getLessonIsNotExist() throws Exception { - Assert.assertTrue(excellent.getWeekTable().getDay(0).getLesson(5).isNotExist()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(1).isNotExist()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isNotExist()); - - Assert.assertFalse(full.getWeekTable().getDay(1).getLesson(12).isAbsenceUnexcused()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceUnexcused()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(5).isAbsenceUnexcused()); - } - - @Test - public void getLessonIsEmpty() throws Exception { - Assert.assertTrue(excellent.getWeekTable().getDay(0).getLesson(0).isEmpty()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(6).isEmpty()); - Assert.assertTrue(excellent.getWeekTable().getDay(4).getLesson(12).isEmpty()); - - Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(9).isEmpty()); - Assert.assertFalse(full.getWeekTable().getDay(2).getLesson(5).isEmpty()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(2).isEmpty()); - } - - @Test - public void getLessonIsPresence() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(7).isPresence()); - Assert.assertTrue(excellent.getWeekTable().getDay(1).getLesson(1).isPresence()); - Assert.assertTrue(excellent.getWeekTable().getDay(3).getLesson(7).isPresence()); - - Assert.assertTrue(full.getWeekTable().getDay(0).getLesson(1).isPresence()); - Assert.assertTrue(full.getWeekTable().getDay(2).getLesson(6).isPresence()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(7).isPresence()); - } - - @Test - public void getLessonIsAbsenceUnexcused() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(7).isAbsenceUnexcused()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(0).isAbsenceUnexcused()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(4).isAbsenceUnexcused()); - - Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(8).isAbsenceUnexcused()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceUnexcused()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isAbsenceUnexcused()); - } - - @Test - public void getLessonIsAbsenceExcused() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(7).isAbsenceExcused()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(0).isAbsenceExcused()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(4).isAbsenceExcused()); - - Assert.assertFalse(full.getWeekTable().getDay(2).getLesson(5).isAbsenceExcused()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceExcused()); - Assert.assertTrue(full.getWeekTable().getDay(4).getLesson(3).isAbsenceExcused()); - } - - @Test - public void getLessonIsAbsenceForSchoolReasons() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isAbsenceForSchoolReasons()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isAbsenceForSchoolReasons()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isAbsenceForSchoolReasons()); - - Assert.assertTrue(full.getWeekTable().getDay(2).getLesson(5).isAbsenceForSchoolReasons()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceForSchoolReasons()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isAbsenceForSchoolReasons()); - } - - @Test - public void getLessonIsUnexcusedLateness() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isUnexcusedLateness()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isUnexcusedLateness()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isUnexcusedLateness()); - - Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(6).isUnexcusedLateness()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isUnexcusedLateness()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isUnexcusedLateness()); - } - - @Test - public void getLessonIsExcusedLateness() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isExcusedLateness()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isExcusedLateness()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isExcusedLateness()); - - Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(7).isExcusedLateness()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isExcusedLateness()); - Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isExcusedLateness()); - } - - @Test - public void getLessonIsExemption() throws Exception { - Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isExemption()); - Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isExemption()); - Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isExemption()); - - Assert.assertFalse(full.getWeekTable().getDay(2).getLesson(5).isExemption()); - Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isExemption()); - Assert.assertTrue(full.getWeekTable().getDay(4).getLesson(8).isExemption()); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java b/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java deleted file mode 100644 index 0a99c465..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java +++ /dev/null @@ -1,111 +0,0 @@ -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; - - private ExamsWeek empty; - - @Before - public void getCurrent() throws Exception { - onePerDay = new ExamsWeek(getSnp("Sprawdziany-one-per-day.html")); - empty = new ExamsWeek(getSnp("Sprawdziany-empty.html")); - } - - @Test - public void getWeekTest() throws Exception { - Assert.assertEquals("2017-10-23", onePerDay.getCurrent().getStartDayDate()); - Assert.assertEquals("2018-04-30", empty.getCurrent().getStartDayDate()); - } - - @Test - public void getDaysListTest() throws Exception { - Assert.assertEquals(3, onePerDay.getCurrent().getDays().size()); - Assert.assertEquals(7, onePerDay.getWeek("", false).getDays().size()); - Assert.assertEquals(0, empty.getCurrent().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("2017-10-23", dayList.get(0).getDate()); - Assert.assertEquals("2017-10-24", dayList.get(1).getDate()); - Assert.assertEquals("2017-10-27", dayList.get(2).getDate()); - } - - @Test - public void getDayNameTest() throws Exception { - List dayList = onePerDay.getCurrent().getDays(); - - Assert.assertEquals("Poniedziałek", dayList.get(0).getDayName()); - Assert.assertEquals("Wtorek", dayList.get(1).getDayName()); - Assert.assertEquals("Piątek", dayList.get(2).getDayName()); - } - - @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("2017-10-16", dayList.get(0).getExamList().get(0).getEntryDate()); - Assert.assertEquals("2017-10-17", dayList.get(1).getExamList().get(0).getEntryDate()); - Assert.assertEquals("2017-10-16", 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 deleted file mode 100644 index deab7617..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java +++ /dev/null @@ -1,106 +0,0 @@ -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(7, 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()); - Assert.assertEquals("+Odp", list.get(6).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()); - Assert.assertEquals("Kordian", list.get(6).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()); - } -} 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 deleted file mode 100644 index d3228378..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/grades/SubjectsListTest.java +++ /dev/null @@ -1,82 +0,0 @@ -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 deleted file mode 100644 index 86a3ef40..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java +++ /dev/null @@ -1,131 +0,0 @@ -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 Document getFixtureAsDocument(String fixtureFileName) { - return Jsoup.parse(getFixtureAsString(fixtureFileName)); - } - - private String getFixtureAsString(String fixtureFileName) { - return FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName)); - } - - private Client getClient(String fixtureFileName) throws Exception { - Document doc = getFixtureAsDocument(fixtureFileName); - - Client client = Mockito.mock(Client.class); - Mockito.when(client.postPageByUrl(Mockito.anyString(), Mockito.any(String[][].class))).thenReturn(doc); - Mockito.when(client.getPageByUrl(Mockito.anyString(), Mockito.anyBoolean())).thenReturn(doc); - - return client; - } - - @Test - public void loginTest() throws Exception { - Client client = getClient("Logowanie-success.html"); - Mockito.when(client.getPageByUrl(Mockito.anyString(), Mockito.anyBoolean())) - .thenReturn(getFixtureAsDocument("Logowanie-error.html")); - Mockito.when(client.postPageByUrl(Mockito.eq(Login.LOGIN_PAGE_URL), Mockito.any(String[][].class))) - .thenReturn(getFixtureAsDocument("Logowanie-certyfikat.html")); - Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString()); - Mockito.when(client.getSymbol()).thenCallRealMethod(); - Mockito.when(client.getHost()).thenReturn("fakelog.cf"); - Login login = new Login(client); - login.login("a@a", "pswd", "d123"); - - Assert.assertEquals("d123", client.getSymbol()); - } - - @Test(expected = BadCredentialsException.class) - public void sendWrongCredentialsTest() throws Exception { - Client client = getClient("Logowanie-error.html"); - Mockito.when(client.getPageByUrl(Mockito.anyString(), Mockito.anyBoolean())) - .thenReturn(getFixtureAsDocument("Logowanie-error.html")); // -error.html because it html with form used by - Login login = new Login(client); - - login.sendCredentials("a@a", "pswd"); - } - - @Test - public void sendCredentialsCertificateTest() throws Exception { - Client client = getClient("Logowanie-certyfikat.html"); - Mockito.when(client.getPageByUrl(Mockito.anyString(), Mockito.anyBoolean())) - .thenReturn(getFixtureAsDocument("Logowanie-error.html")); // -error.html because it html with form used by - Login login = new Login(client); - - Assert.assertEquals( - getFixtureAsString("cert-stock.xml").replaceAll("\\s+", ""), - login.sendCredentials("a@a", "passwd") - .select("input[name=wresult]") - .attr("value") - .replaceAll("\\s+", "") - ); - } - - @Test - public void sendCertificateNotDefaultSymbolSuccessTest() throws Exception { - Client client = getClient("Logowanie-success.html"); - Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString()); - Mockito.when(client.getSymbol()).thenCallRealMethod(); - Mockito.when(client.getHost()).thenReturn("fakelog.cf"); - Login login = new Login(client); - - login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "wulkanowyschool321"); - - Assert.assertEquals("wulkanowyschool321", client.getSymbol()); - } - - @Test - public void sendCertificateDefaultSymbolSuccessTest() throws Exception { - Client client = getClient("Logowanie-success.html"); - Mockito.doCallRealMethod().when(client).setSymbol(Mockito.anyString()); - Mockito.when(client.getSymbol()).thenCallRealMethod(); - Mockito.when(client.getHost()).thenReturn("fakelog.cf"); - Login login = new Login(client); - - login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "Default"); - - Assert.assertEquals("demo12345", client.getSymbol()); - } - - @Test(expected = AccountPermissionException.class) - public void sendCertificateAccountPermissionTest() throws Exception { - Client client = getClient("Logowanie-brak-dostepu.html"); - - Login login = new Login(client); - - login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "demo123"); - } - - @Test(expected = LoginErrorException.class) - public void sendCertificateLoginErrorTest() throws Exception { - Login login = new Login(getClient("Logowanie-certyfikat.html")); // change to other document - - login.sendCertificate(getFixtureAsDocument("Logowanie-certyfikat.html"), "demo123"); - } - - @Test - public void findSymbolInCertificateTest() throws Exception { - Login login = new Login(getClient("Logowanie-certyfikat.html")); - - String certificate = getFixtureAsString("cert-stock.xml"); - - Assert.assertEquals("demo12345", login.findSymbolInCertificate(certificate)); - } - - @Test(expected = AccountPermissionException.class) - public void findSymbolInCertificateWithoutSecondInstanceTest() throws Exception { - Login login = new Login(getClient("Logowanie-certyfikat.html")); - - login.findSymbolInCertificate(getFixtureAsString("cert-no-symbols.xml")); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/login/StartPageTest.kt b/api/src/test/java/io/github/wulkanowy/api/login/StartPageTest.kt deleted file mode 100644 index 271b3bb0..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/login/StartPageTest.kt +++ /dev/null @@ -1,68 +0,0 @@ -package io.github.wulkanowy.api.login - -import io.github.wulkanowy.api.Client -import io.github.wulkanowy.api.FixtureHelper -import io.github.wulkanowy.api.VulcanException -import org.jsoup.Jsoup -import org.jsoup.nodes.Document -import org.junit.Assert.assertEquals -import org.junit.Before -import org.junit.Test -import org.mockito.Mockito -import org.mockito.Mockito.mock - -class StartPageTest { - - private val client: Client = mock(Client::class.java) - - @Before fun setUp() { - Mockito.`when`(client.host).thenReturn("fakelog.cf") - } - - private fun getDoc(name: String): Document = Jsoup.parse(FixtureHelper.getAsString(javaClass.getResourceAsStream(name))) - - @Test fun getSchoolTest() { - assertEquals("534213", StartPage(client).getSchools(getDoc("../Start-std.html"))[0].id) - } - - @Test fun getMultiSchoolTest() { - val schools = StartPage(client).getSchools(getDoc("../Start-multi.html")) - - assertEquals("123456", schools[0].id) - assertEquals("123457", schools[1].id) - } - - @Test fun getSchoolNameTest() { - assertEquals("Uczeń", StartPage(client).getSchools(getDoc("../Start-std.html"))[0].name) - } - - @Test fun getMultiSchoolNameTest() { - val schools = StartPage(client).getSchools(getDoc("../Start-multi.html")) - - assertEquals("GIMBB", schools[0].name) - assertEquals("SPBB", schools[1].name) - } - - @Test(expected = VulcanException::class) - fun getSnpPageUrlWithWrongPage() { - StartPage(client).getSchools(getDoc("../OcenyWszystkie-semester.html")) - } - - @Test - fun getExtractedIDStandardTest() { - assertEquals("123456", StartPage(client) - .getExtractedIdFromUrl("https://uonetplus-opiekun.fakelog.cf/powiat/123456/Start/Index/")) - } - - @Test - fun getExtractedIDDemoTest() { - assertEquals("demo12345", StartPage(client) - .getExtractedIdFromUrl("https://uonetplus-opiekun.fakelog.cf/demoupowiat/demo12345/Start/Index/")) - } - - @Test(expected = VulcanException::class) - fun getExtractedIDNotLoggedTest() { - assertEquals("123", StartPage(client) - .getExtractedIdFromUrl("https://uonetplus.NOTfakelog.cf/powiat/")) - } -} 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 deleted file mode 100644 index 14fa4627..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/messages/MessagesTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.wulkanowy.api.messages; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import java.util.List; - -import io.github.wulkanowy.api.Client; -import io.github.wulkanowy.api.FixtureHelper; -import io.github.wulkanowy.api.NotLoggedInErrorException; - -public class MessagesTest { - - private Client getFixtureAsString(String fixtureFileName) throws Exception { - Client client = Mockito.mock(Client.class); - Mockito.when(client.getJsonStringByUrl(Mockito.anyString())) - .thenReturn(FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName))); - Mockito.when(client.postJsonStringByUrl(Mockito.anyString(), Mockito.any())) - .thenReturn(FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName))); - return client; - } - - @Test - public void getMessages() throws Exception { - Client client = getFixtureAsString("GetWiadomosciOdebrane.json"); - - Messages messages = new Messages(client); - List messageList = messages.getReceived(); - - Assert.assertEquals(true, messageList.get(1).unread); - Assert.assertEquals("2016-03-15 09:00:00", messageList.get(0).date); - Assert.assertEquals(null, messageList.get(0).content); - Assert.assertEquals("Kowalski Jan", messageList.get(0).sender); - Assert.assertEquals(12347, messageList.get(2).id); - } - - @Test - public void getMessagesEmpty() throws Exception { - Client client = getFixtureAsString("GetWiadomosciUsuniete-empty.json"); - - Messages messages = new Messages(client); - List messageList = messages.getSent(); - - Assert.assertTrue(messageList.isEmpty()); - } - - @Test(expected = NotLoggedInErrorException.class) - public void getMessagesError() throws Exception { - Client client = getFixtureAsString("UndefinedError.txt"); - - Messages messages = new Messages(client); - messages.getDeleted(); - } - - @Test(expected = BadRequestException.class) - public void getMessagesBadRequest() throws Exception { - Client client = getFixtureAsString("PageError.html"); - - Messages messages = new Messages(client); - messages.getDeleted(); - } - - @Test - public void getMessage() throws Exception { - Client client = getFixtureAsString("GetTrescWiadomosci.json"); - - Messages messages = new Messages(client); - Message message = messages.getMessage(123, Messages.RECEIVED_FOLDER); - Assert.assertEquals(12345, message.id); - Assert.assertEquals("Witam, …. \nPozdrawiam Krzysztof Czerkas", message.content); - } - - @Test(expected = NotLoggedInErrorException.class) - public void getMessageError() throws Exception { - Client client = getFixtureAsString("UndefinedError.txt"); - - Messages messages = new Messages(client); - messages.getMessage(321, Messages.SENT_FOLDER); - } - - @Test(expected = BadRequestException.class) - public void getMessageBadRequest() throws Exception { - Client client = getFixtureAsString("PageError.html"); - - Messages messages = new Messages(client); - messages.getMessage(1, Messages.DELETED_FOLDER); - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt b/api/src/test/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt deleted file mode 100644 index f67310c8..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.api.mobile - -import io.github.wulkanowy.api.StudentAndParentTestCase -import org.junit.Assert.assertEquals -import org.junit.Test - -class RegisterDeviceTest : StudentAndParentTestCase() { - - @Test - fun getTokenTest() { - val registration = RegisterDevice(getSnp("Rejestruj.html")) - - assertEquals("3S1A1B2C", registration.getToken().token) - assertEquals("Default", registration.getToken().symbol) - assertEquals("1234567", registration.getToken().pin) - } -} diff --git a/api/src/test/java/io/github/wulkanowy/api/mobile/RegisteredDevicesListTest.kt b/api/src/test/java/io/github/wulkanowy/api/mobile/RegisteredDevicesListTest.kt deleted file mode 100644 index 3cd8b97c..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/mobile/RegisteredDevicesListTest.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.api.mobile - -import io.github.wulkanowy.api.StudentAndParentTestCase -import org.junit.Assert.assertEquals -import org.junit.Test - -class RegisteredDevicesListTest : StudentAndParentTestCase() { - - private val filled = RegisteredDevices(getSnp("DostepMobilny-filled.html")) - - @Test - fun getListTest() { - assertEquals(2, filled.getList().size) - } - - @Test - fun getNameTest() { - assertEquals("google Android SDK built for x86", filled.getList()[0].name) - assertEquals("google (Android SDK) built for x86", filled.getList()[1].name) - } - - @Test - fun getSystemTest() { - assertEquals("Android 8.1.0", filled.getList()[0].system) - assertEquals("Android 8.1.0", filled.getList()[1].system) - } - - @Test - fun getDateTest() { - assertEquals("2018-01-20 22:35:30", filled.getList()[0].date) - } - - @Test - fun getIdTest() { - assertEquals(321, filled.getList()[0].id) - } -} 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 deleted file mode 100644 index 0d7774bb..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/notes/AchievementsListTest.java +++ /dev/null @@ -1,36 +0,0 @@ -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 deleted file mode 100644 index 304eb01a..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java +++ /dev/null @@ -1,60 +0,0 @@ -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("2017-06-06", filledList.get(0).getDate()); - Assert.assertEquals("2016-10-01", 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 deleted file mode 100644 index 947db9b8..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/school/SchoolInfoTest.java +++ /dev/null @@ -1,48 +0,0 @@ -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 deleted file mode 100644 index b0da356b..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/school/TeachersInfoTest.java +++ /dev/null @@ -1,56 +0,0 @@ -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 deleted file mode 100644 index 7b3cce6e..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java +++ /dev/null @@ -1,253 +0,0 @@ -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 fizyczne", full.getWeekTable().getDay(0).getLesson(9).getSubject()); - Assert.assertEquals("Wychowanie do życia w rodzinie", full.getWeekTable().getDay(2).getLesson(0).getSubject()); - Assert.assertEquals("Wychowanie fizyczne", full.getWeekTable().getDay(3).getLesson(1).getSubject()); - Assert.assertEquals("Uroczyste zakończenie roku szkolnego", full.getWeekTable().getDay(4).getLesson(0).getSubject()); - Assert.assertEquals("Fizyka", full.getWeekTable().getDay(0).getLesson(0).getSubject()); - Assert.assertEquals("Metodologia programowania", full.getWeekTable().getDay(1).getLesson(0).getSubject()); - Assert.assertEquals("Język niemiecki", full.getWeekTable().getDay(4).getLesson(2).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("", full.getWeekTable().getDay(4).getLesson(1).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("", full.getWeekTable().getDay(4).getLesson(1).getDescription()); - Assert.assertEquals("bez nawiasów (poprzednio: Religia)", full.getWeekTable().getDay(4).getLesson(3).getDescription()); - Assert.assertEquals("poprzednio: Wychowanie fizyczne", full.getWeekTable().getDay(4).getLesson(2).getDescription()); - Assert.assertEquals("egzamin", full.getWeekTable().getDay(3).getLesson(0).getDescription()); - Assert.assertEquals("", full.getWeekTable().getDay(4).getLesson(1).getDescription()); - Assert.assertEquals("poprzednio: Zajęcia z wychowawcą", full.getWeekTable().getDay(4).getLesson(5).getDescription()); - Assert.assertEquals("opis w uwadze bez klasy w spanie", full.getWeekTable().getDay(4).getLesson(4).getDescription()); - Assert.assertEquals("", holidays.getWeekTable().getDay(3).getLesson(3).getDescription()); - } - - @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(full.getWeekTable().getDay(4).getLesson(1).isNewMovedInOrChanged()); - Assert.assertTrue(full.getWeekTable().getDay(4).getLesson(2).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 deleted file mode 100644 index 6b729c95..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/user/BasicInformationTest.java +++ /dev/null @@ -1,106 +0,0 @@ -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 deleted file mode 100644 index b4f858ec..00000000 --- a/api/src/test/java/io/github/wulkanowy/api/user/FamilyInformationTest.java +++ /dev/null @@ -1,59 +0,0 @@ -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 deleted file mode 100644 index dc7c6c3b..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/OcenyWszystkie-semester.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Witryna ucznia i rodzica – Oceny - - -
-

Oceny

-
-
- - -
-
-
-
wersja: 17.05.0000.24042
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/Start-multi.html b/api/src/test/resources/io/github/wulkanowy/api/Start-multi.html deleted file mode 100644 index d5323677..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/Start-multi.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - Uonet+ - - -
-
-
-
- -
-
-
-
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/Start-std.html b/api/src/test/resources/io/github/wulkanowy/api/Start-std.html deleted file mode 100644 index b65625cf..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/Start-std.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - Uonet+ - - -
-
- -
-
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/WitrynaUczniaIRodzica.html b/api/src/test/resources/io/github/wulkanowy/api/WitrynaUczniaIRodzica.html deleted file mode 100644 index 66e2942f..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/WitrynaUczniaIRodzica.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Witryna ucznia i rodzica – Strona główna - - -
    -
  • - - -
  • -
  • - - -
  • -
- -
wersja: 17.09.0008.26553
- - 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 deleted file mode 100644 index 5eaf50df..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-excellent.html +++ /dev/null @@ -1,408 +0,0 @@ - - - - - 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 deleted file mode 100644 index aa995305..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-full.html +++ /dev/null @@ -1,498 +0,0 @@ - - - - - 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-empty.html b/api/src/test/resources/io/github/wulkanowy/api/exams/Sprawdziany-empty.html deleted file mode 100644 index bf1032b4..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/exams/Sprawdziany-empty.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Witryna ucznia i rodzica – Terminarz sprawdzianów - - -
-

Sprawdziany

-

Tydzień 30.04.2018 - 06.05.2018

-

Nie zaplanowano żadnych sprawdzianów na wybrany tydzień

- -
-
wersja: 17.09.0009.26859
- - 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 deleted file mode 100644 index ecc48cf1..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/exams/Sprawdziany-one-per-day.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - 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 deleted file mode 100644 index d0cdb664..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-filled.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - 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ń
Język polski5+Odp, Kordian5,0011.05.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 deleted file mode 100644 index 023b5405..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects-average.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - 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 deleted file mode 100644 index 0f6da414..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/grades/OcenyWszystkie-subjects.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - 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 deleted file mode 100644 index 2911f1d2..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-brak-dostepu.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - 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 deleted file mode 100644 index f53a3485..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-certyfikat.html +++ /dev/null @@ -1,17 +0,0 @@ - - - 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 deleted file mode 100644 index afb044d7..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-error.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - Logowanie (demo123) - - -
-
-
-

Logowanie

-
-
- Zła nazwa użytkownika lub hasło -
-
-
- - - diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-notLoggedIn.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-notLoggedIn.html deleted file mode 100644 index f961bf82..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-notLoggedIn.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Dziennik UONET+ - - -
-
-
-
-
- - Zaloguj się -
-
-
-
Uonet+ wersja 17.09.0007.26300
-
-
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-success.html b/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-success.html deleted file mode 100644 index b635388b..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/Logowanie-success.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - 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 deleted file mode 100644 index 062f9b60..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/PrzerwaTechniczna.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - 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-no-symbols.xml b/api/src/test/resources/io/github/wulkanowy/api/login/cert-no-symbols.xml deleted file mode 100644 index ca14bdf5..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/cert-no-symbols.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Default - - - - - - diff --git a/api/src/test/resources/io/github/wulkanowy/api/login/cert-stock.xml b/api/src/test/resources/io/github/wulkanowy/api/login/cert-stock.xml deleted file mode 100644 index 31aa19b2..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/login/cert-stock.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - 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 deleted file mode 100644 index 1ba54a32..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/messages/GetTrescWiadomosci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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 deleted file mode 100644 index 326390ed..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciOdebrane.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "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 deleted file mode 100644 index 36f89aa1..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/messages/GetWiadomosciUsuniete-empty.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "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 deleted file mode 100644 index ae976af1..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/messages/PageError.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - 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 deleted file mode 100644 index c8d55a96..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/messages/UndefinedError.txt +++ /dev/null @@ -1 +0,0 @@ -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/mobile/DostepMobilny-filled.html b/api/src/test/resources/io/github/wulkanowy/api/mobile/DostepMobilny-filled.html deleted file mode 100644 index 9fbfd403..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/mobile/DostepMobilny-filled.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - Witryna ucznia i rodzica – dostęp mobilny - - - -
-

Dostęp mobilny

- -
-

Zarejestrowane urządzenia

-
- - - - - - - - - - - - - - - - - - - - -
UrządzenieData rejestracji
google Android SDK built for x86 (Android 8.1.0)20.01.2018 godz: 22:35:30 - Wyrejestruj -
google (Android SDK) built for x86 (Android 8.1.0)20.01.2018 godz: 22:35:30 - Wyrejestruj -
-
- -
wersja: 18.01.0001.27311
- - diff --git a/api/src/test/resources/io/github/wulkanowy/api/mobile/Rejestruj.html b/api/src/test/resources/io/github/wulkanowy/api/mobile/Rejestruj.html deleted file mode 100644 index 15d08d07..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/mobile/Rejestruj.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - Witryna ucznia i rodzica – Rejestracja urządzenia mobilnego - - - -
-

Rejestracja urządzenia mobilnego

-
- Za pomocą aplikacji "Dzienniczek+" zeskanuj kod QR. - Kod QR - Token: 3S1A1B2C - Symbol: Default - PIN: 1234567 -
-
-
wersja: 18.01.0001.27311
- - 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 deleted file mode 100644 index b15bb6e7..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-empty.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - 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 deleted file mode 100644 index 50e74058..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/notes/UwagiOsiagniecia-filled.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - 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 deleted file mode 100644 index 05a698a0..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/school/Szkola.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - 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 deleted file mode 100644 index c3dbfebe..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-full.html +++ /dev/null @@ -1,567 +0,0 @@ - - - - - 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 - -
-
-
- Wychowanie fizyczne [zaw2] - - - G3 - (przeniesiona z lekcji 7, 01.12.2017) -
-
- Metodologia programowania [zaw2] - - Baran Małgorzata - 36 - (zmiana organizacji zajęć) -
-
-
- Użytkowanie urządzeń peryferyjnych komputera [zaw2] - - Bączek Robert - -
-
-
- Wychowanie fizyczne [zaw1] - - Jarocki Krzysztof - G4 - Wychowanie fizyczne [zaw1] - - Nowicka Irena - G4 - (zastępstwo) -
-
-
- Uroczyste rozpoczecie roku szkolnego 2017/2018 - - -
-
- Uroczyste rozpoczecie roku szkolnego 2017/2018 - - -
-
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 - -
-
-
- Język niemiecki [wf_grupa_2] - - - -
-
- Wychowanie fizyczne [wf_grupa_2] - - Nauczycielel - 106 -
-
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 - -
-
-
- Religia - Cyranka Krystian - 3 - Wychowanie do życia w rodzinie - Nowak Jadwiga - 3 - bez nawiasów -
-
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 - -
-
-
- Język polski - - 16 - (oddział nieobecny) -
- -
opis w uwadze bez klasy w spanie
-
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 - -
-
-
- Tworzenie i administrowanie bazami danych [zaw2] - - - -
-
- Zajęcia z wychowawcą - Małgorzata Kowal - 43 - (zmiana organizacji zajęć) -
-
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 -
- Wychowanie fizyczne [zaw2] - - - G3 - (przeniesiona z lekcji 7, 01.12.2017) -
-
- -
-
-
- 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 deleted file mode 100644 index 09555cf3..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-holidays.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - 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 deleted file mode 100644 index 8bcc9794..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/timetable/PlanLekcji-std.html +++ /dev/null @@ -1,303 +0,0 @@ - - - - - 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 deleted file mode 100644 index c54dd861..00000000 --- a/api/src/test/resources/io/github/wulkanowy/api/user/UczenDanePodstawowe.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - 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 77934331..e88f74c6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,56 +1,39 @@ -apply plugin: 'org.greenrobot.greendao' apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' // sync warning probably caused by bug https://issuetracker.google.com/issues/74537216 +apply plugin: 'kotlin-kapt' +apply plugin: 'kotlin-android-extensions' apply plugin: 'io.fabric' -apply from: 'jacoco.gradle' -apply from: 'android-sonarqube.gradle' -apply plugin: 'com.google.gms.oss.licenses.plugin' apply plugin: 'com.github.triplet.play' - -buildscript { - repositories { - maven { url "https://plugins.gradle.org/m2/" } - maven { url 'https://maven.fabric.io/public' } - google() - } - - dependencies { - classpath "org.greenrobot:greendao-gradle-plugin:$greenDaoGradle" - classpath "io.fabric.tools:gradle:$fabricGradle" - classpath "com.google.gms:oss-licenses:0.9.2" - classpath "com.github.triplet.gradle:play-publisher:$playPublisher" - } -} - -repositories { - maven { url 'https://maven.fabric.io/public' } -} +apply from: 'jacoco.gradle' +apply from: 'sonarqube.gradle' android { - compileSdkVersion 27 - buildToolsVersion '27.0.3' - - playAccountConfigs { - defaultAccountConfig { - serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") - pk12File = file('key.p12') - } - } + compileSdkVersion 28 + buildToolsVersion '28.0.3' defaultConfig { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 - targetSdkVersion 27 - versionCode 16 - versionName "0.5.2" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + targetSdkVersion 28 + versionCode 38 + versionName "0.9.0" + multiDexEnabled true + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true - playAccountConfig = playAccountConfigs.defaultAccountConfig manifestPlaceholders = [ - fabricApiKey: System.getenv("FABRIC_API_KEY") ?: "null" + fabric_api_key : System.getenv("FABRIC_API_KEY") ?: "null", + crashlytics_enabled: project.hasProperty("enableCrashlytics") ] + javaCompileOptions { + annotationProcessorOptions { + arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] + } + } + } + + sourceSets { + androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } signingConfigs { @@ -64,81 +47,126 @@ android { buildTypes { release { - minifyEnabled false + buildConfigField "boolean", "CRASHLYTICS_ENABLED", "true" + minifyEnabled true + shrinkResources true 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 = false + ext.enableCrashlytics = project.hasProperty("enableCrashlytics") + multiDexKeepProguard file('proguard-multidex-rules.pro') } } - testOptions { - unitTests.all { - testLogging { - events "passed", "skipped", "failed", "standardOut", "standardError" - outputs.upToDateWhen { false } - showStandardStreams = true - } + flavorDimensions "platform" + productFlavors { + play { + dimension "platform" + } + + fdroid { + buildConfigField "boolean", "CRASHLYTICS_ENABLED", "false" + dimension "platform" } } + + lintOptions { + disable 'HardwareIds' + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +androidExtensions { + experimental = true } play { + serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") ?: "jan@fakelog.cf" + serviceAccountCredentials = file('key.p12') + defaultToAppBundles = false track = 'alpha' - uploadImages = true -} - -greendao { - schemaVersion 29 - generateTests = true -} - -configurations.all { - resolutionStrategy.force "com.android.support:support-annotations:$supportVersion" } dependencies { - implementation project(':api') + implementation 'io.github.wulkanowy:api:0.9.0' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation "com.android.support:support-v4:$supportVersion" - implementation "com.android.support:design:$supportVersion" - implementation "com.android.support:cardview-v7:$supportVersion" - implementation "com.android.support:customtabs:$supportVersion" - implementation "com.android.support:preference-v14:$supportVersion" - implementation "com.firebase:firebase-jobdispatcher:$firebaseJob" - implementation "org.apache.commons:commons-lang3:$apacheLang" - implementation "org.apache.commons:commons-collections4:$apacheCollections" - implementation "eu.davidea:flexible-adapter:$flexibleAdapter" - implementation "eu.davidea:flexible-adapter-ui:$flexibleUi" - implementation "org.greenrobot:greendao:$greenDao" - implementation "com.jakewharton:butterknife:$butterknife" - implementation "com.google.dagger:dagger-android-support:$dagger2" - implementation "com.aurelhubert:ahbottomnavigation:$ahbottom" - implementation "com.jakewharton.threetenabp:threetenabp:$threeTenABP" - implementation "com.google.android.gms:play-services-oss-licenses:$ossLicenses" - implementation "com.jakewharton.timber:timber:$timber" - implementation "at.favre.lib:slf4j-timber:$slf4jTimber" + implementation "androidx.legacy:legacy-support-v4:1.0.0" + implementation "androidx.appcompat:appcompat:1.0.2" + implementation 'androidx.multidex:multidex:2.0.1' - implementation("com.crashlytics.sdk.android:crashlytics:$crashlyticsSdk@aar") { - transitive = true - } - implementation("com.crashlytics.sdk.android:answers:$crashlyticsAnswers@aar") { - transitive = true + implementation "androidx.cardview:cardview:1.0.0" + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation "com.google.android.material:material:1.1.0-alpha07" + implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f' + implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + + implementation "androidx.work:work-runtime:2.0.1" + implementation "androidx.work:work-rxjava2:2.0.1" + + implementation "androidx.room:room-runtime:2.1.0-rc01" + implementation "androidx.room:room-rxjava2:2.1.0-rc01" + kapt "androidx.room:room-compiler:2.1.0-rc01" + + implementation "com.google.dagger:dagger-android-support:2.23.1" + kapt "com.google.dagger:dagger-compiler:2.23.1" + kapt "com.google.dagger:dagger-android-processor:2.23.1" + implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0' + kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0' + + implementation "eu.davidea:flexible-adapter:5.1.0" + implementation "eu.davidea:flexible-adapter-ui:1.0.0" + implementation "com.aurelhubert:ahbottomnavigation:2.3.4" + implementation 'com.ncapdevi:frag-nav:3.2.0' + + implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.3' + implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' + implementation "io.reactivex.rxjava2:rxjava:2.2.9" + + implementation 'com.google.code.gson:gson:2.8.5' + implementation "com.jakewharton.threetenabp:threetenabp:1.2.0" + implementation "com.jakewharton.timber:timber:4.7.1" + implementation "at.favre.lib:slf4j-timber:1.0.1" + implementation "com.squareup.okhttp3:logging-interceptor:3.12.3" + + implementation "com.mikepenz:aboutlibraries:6.2.3" + implementation 'com.takisoft.preferencex:preferencex:1.0.0' + + playImplementation 'com.google.firebase:firebase-core:16.0.9' + playImplementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' + + releaseImplementation 'fr.o80.chucker:library-no-op:2.0.4' + + debugImplementation 'fr.o80.chucker:library:2.0.4' + debugImplementation "com.amitshekhar.android:debug-db:1.0.6" + + testImplementation "junit:junit:4.12" + testImplementation "io.mockk:mockk:1.9.2" + testImplementation 'org.threeten:threetenbp:1.4.0' + testImplementation "org.mockito:mockito-core:2.28.2" + testImplementation("org.mockito:mockito-inline:2.28.2") { + exclude group: 'org.mockito', module: 'mockito-core' } - kapt "com.google.dagger:dagger-compiler:$dagger2" - kapt "com.google.dagger:dagger-android-processor:$dagger2" - kapt "com.jakewharton:butterknife-compiler:$butterknife" - - debugImplementation "com.amitshekhar.android:debug-db:$debugDb" - - testImplementation "junit:junit:$junit" - testImplementation "org.mockito:mockito-core:$mockito" - - androidTestImplementation "com.android.support.test:runner:$testRunner" - androidTestImplementation "org.mockito:mockito-android:$mockito" + androidTestImplementation 'androidx.test:core:1.2.0' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation "io.mockk:mockk-android:1.9.2" + androidTestImplementation "androidx.room:room-testing:2.1.0-rc01" + androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + androidTestImplementation "org.mockito:mockito-core:2.28.2" + androidTestImplementation('org.mockito:mockito-android:2.28.2') { + exclude group: 'org.mockito', module: 'mockito-core' + } } + +apply plugin: 'com.google.gms.google-services' diff --git a/app/jacoco.gradle b/app/jacoco.gradle index f59b3857..44cd3f62 100644 --- a/app/jacoco.gradle +++ b/app/jacoco.gradle @@ -1,7 +1,7 @@ apply plugin: "jacoco" jacoco { - toolVersion "0.8.1" + toolVersion "0.8.3" reportsDir = file("$buildDir/reports") } @@ -9,7 +9,6 @@ tasks.withType(Test) { jacoco.includeNoLocationClasses = true } -// run ./gradlew clean createDebugCoverageReport jacocoTestReport task jacocoTestReport(type: JacocoReport) { group = "Reporting" @@ -20,37 +19,32 @@ task jacocoTestReport(type: JacocoReport) { html.enabled = true } - def excludes = [ - "**/R.class", - "**/R\$*.class", - "**/*\$ViewInjector*.*", - "**/BuildConfig.*", - "**/Manifest*.*", - "**/*Test*.*", - "android/**/*.*", - "**/*Fragment.*", - "**/*Activity.*" - ] + def excludes = ['**/R.class', + '**/R$*.class', + '**/BuildConfig.*', + '**/Manifest*.*', + '**/*Test*.*', + 'android/**/*.*', + '**/*Module.*', + '**/*Dagger*.*', + '**/*MembersInjector*.*', + '**/*_Provide*Factory*.*', + '**/*_Factory.*'] - // generated classes - classDirectories = fileTree( - // Java generated classes on Android project (debug build) + classDirectories.setFrom(fileTree( dir: "$buildDir/intermediates/classes/debug", excludes: excludes ) + fileTree( - // Kotlin generated classes on Android project (debug build) - dir: "$buildDir/tmp/kotlin-classes/debug", + dir: "$buildDir/tmp/kotlin-classes/playDebug", excludes: excludes - ) + )) - // sources - sourceDirectories = files([ - android.sourceSets.main.java.srcDirs, - "src/main/kotlin" - ]) - - executionData = fileTree( + sourceDirectories.setFrom(files([ + "src/main/java", + "src/play/java" + ])) + executionData.setFrom(fileTree( dir: project.projectDir, - includes: ["**/*.exec" , "**/*.ec"] - ) + includes: ["**/*.exec", "**/*.ec"] + )) } diff --git a/app/key.p12.gpg b/app/key.p12.gpg new file mode 100644 index 00000000..e9b6d06e Binary files /dev/null and b/app/key.p12.gpg differ diff --git a/app/proguard-multidex-rules.pro b/app/proguard-multidex-rules.pro new file mode 100644 index 00000000..9ee1737f --- /dev/null +++ b/app/proguard-multidex-rules.pro @@ -0,0 +1,3 @@ +-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 34ed4c38..15b62838 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,25 +1,39 @@ -# 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 +# Optimizations +-optimizationpasses 5 +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontskipnonpubliclibraryclassmembers +-dontpreverify +-dontobfuscate +-allowaccessmodification +-repackageclasses '' +-verbose -# Add any project specific keep options here: -# 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 *; -#} +#Config for anallitycs +-keepattributes *Annotation* +-keepattributes SourceFile,LineNumberTable +-keep class com.crashlytics.** {*;} +-keep public class * extends java.lang.Exception +-dontwarn com.crashlytics.** -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile +#Config for OkHttp +-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase +-dontwarn org.codehaus.mojo.animal_sniffer.* +-dontwarn okhttp3.internal.platform.ConscryptPlatform +-dontwarn javax.annotation.** + + +#Config for ReactiveNetwork +-dontwarn com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +-dontwarn io.reactivex.functions.Function +-dontwarn rx.internal.util.** +-dontwarn sun.misc.Unsafe + +#Config for MPAndroidChart +-keep class com.github.mikephil.charting.** { *; } + +#Config for API +-keep class io.github.wulkanowy.api.** {*;} diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json new file mode 100644 index 00000000..e42a4a71 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json @@ -0,0 +1,1325 @@ +{ + "formatVersion": 1, + "database": { + "version": 11, + "identityHash": "478af7daed6ac4563e71826fb70cc8c8", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predictedGrade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "finalGrade", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"478af7daed6ac4563e71826fb70cc8c8\")" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json new file mode 100644 index 00000000..32f94355 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json @@ -0,0 +1,1332 @@ +{ + "formatVersion": 1, + "database": { + "version": 12, + "identityHash": "972ad26e6488d9a8239f6bd8597af61d", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predictedGrade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "finalGrade", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"972ad26e6488d9a8239f6bd8597af61d\")" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json new file mode 100644 index 00000000..ab554665 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json @@ -0,0 +1,1356 @@ +{ + "formatVersion": 1, + "database": { + "version": 13, + "identityHash": "18643bb64804b8268ec9395e3dd55ecb", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predictedGrade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "finalGrade", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"18643bb64804b8268ec9395e3dd55ecb\")" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/14.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/14.json new file mode 100644 index 00000000..82b76492 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/14.json @@ -0,0 +1,1386 @@ +{ + "formatVersion": 1, + "database": { + "version": 14, + "identityHash": "b22945c41e7841ff2e6b16af346dde0c", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b22945c41e7841ff2e6b16af346dde0c')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/15.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/15.json new file mode 100644 index 00000000..6f2d1d1d --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/15.json @@ -0,0 +1,1430 @@ +{ + "formatVersion": 1, + "database": { + "version": 15, + "identityHash": "84b300bf53c7dd70b60a29a842275bb2", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '84b300bf53c7dd70b60a29a842275bb2')" + ] + } +} \ No newline at end of file diff --git a/app/android-sonarqube.gradle b/app/sonarqube.gradle similarity index 73% rename from app/android-sonarqube.gradle rename to app/sonarqube.gradle index b9284eea..3dad1034 100644 --- a/app/android-sonarqube.gradle +++ b/app/sonarqube.gradle @@ -4,16 +4,13 @@ sonarqube { //noinspection GroovyAssignabilityCheck properties { - 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] + def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-28/android.jar" - property "sonar.projectName", GROUP_ID + ":app" - property "sonar.projectKey", GROUP_ID + ":app" + property "sonar.projectName", "io.github.wulkanowy:app" + property "sonar.projectKey", "io.github.wulkanowy:app" property "sonar.sources", "src/main/java" - property "sonar.exclusions", "build/**,**/*.png,*.iml, **/*generated*," + - "src/**/entities/*.java, src/androidTest/**/entities/*.java" + property "sonar.exclusions", "build/**,**/*.png,*.iml, **/*generated*," property "sonar.import_unknown_files", true // Defines where the java files are @@ -32,5 +29,6 @@ sonarqube { property "sonar.java.coveragePlugin", "jacoco" property "sonar.android.lint.report", "build/reports/lint-results.xml" property "sonar.jacoco.reportPaths", fileTree(dir: project.projectDir, includes: ['**/*.exec', '**/*.ec']) + property "sonar.coverage.jacoco.xmlReportPaths", "build/reports/jacocoTestReport/jacocoTestReport.xml" } } 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 deleted file mode 100644 index e5330b62..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AccountTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -public class AccountTest extends AbstractDaoTestLongPk { - - public AccountTest() { - super(AccountDao.class); - } - - @Override - protected Account createEntity(Long key) { - Account entity = new Account(); - entity.setId(key); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java deleted file mode 100644 index 0aca371a..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -public class AttendanceLessonTest extends AbstractDaoTestLongPk { - - public AttendanceLessonTest() { - super(AttendanceLessonDao.class); - } - - @Override - protected AttendanceLesson createEntity(Long key) { - AttendanceLesson entity = new AttendanceLesson(); - entity.setId(key); - entity.setPresence(false); - entity.setAbsenceUnexcused(false); - entity.setAbsenceExcused(false); - entity.setUnexcusedLateness(false); - entity.setAbsenceForSchoolReasons(false); - entity.setExcusedLateness(false); - entity.setExemption(false); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DayTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DayTest.java deleted file mode 100644 index cdab015a..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DayTest.java +++ /dev/null @@ -1,19 +0,0 @@ -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.setFreeDay(false); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DiaryTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DiaryTest.java deleted file mode 100644 index 2951aec8..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DiaryTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -public class DiaryTest extends AbstractDaoTestLongPk { - - public DiaryTest() { - super(DiaryDao.class); - } - - @Override - protected Diary createEntity(Long key) { - Diary entity = new Diary(); - entity.setId(key); - entity.setCurrent(false); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/ExamTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/ExamTest.java deleted file mode 100644 index 271e811f..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/ExamTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -public class ExamTest extends AbstractDaoTestLongPk { - - public ExamTest() { - super(ExamDao.class); - } - - @Override - protected Exam createEntity(Long key) { - Exam entity = new Exam(); - entity.setId(key); - 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 deleted file mode 100644 index ea026559..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/GradeTest.java +++ /dev/null @@ -1,20 +0,0 @@ -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/SchoolTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SchoolTest.java deleted file mode 100644 index 13f63e0d..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SchoolTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -import io.github.wulkanowy.data.db.dao.entities.School; -import io.github.wulkanowy.data.db.dao.entities.SchoolDao; - -public class SchoolTest extends AbstractDaoTestLongPk { - - public SchoolTest() { - super(SchoolDao.class); - } - - @Override - protected School createEntity(Long key) { - School entity = new School(); - entity.setId(key); - entity.setCurrent(false); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SemesterTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SemesterTest.java deleted file mode 100644 index a18a7674..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SemesterTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -import io.github.wulkanowy.data.db.dao.entities.Semester; -import io.github.wulkanowy.data.db.dao.entities.SemesterDao; - -public class SemesterTest extends AbstractDaoTestLongPk { - - public SemesterTest() { - super(SemesterDao.class); - } - - @Override - protected Semester createEntity(Long key) { - Semester entity = new Semester(); - entity.setId(key); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/StudentTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/StudentTest.java deleted file mode 100644 index 1ba8fd46..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/StudentTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -import io.github.wulkanowy.data.db.dao.entities.Student; -import io.github.wulkanowy.data.db.dao.entities.StudentDao; - -public class StudentTest extends AbstractDaoTestLongPk { - - public StudentTest() { - super(StudentDao.class); - } - - @Override - protected Student createEntity(Long key) { - Student entity = new Student(); - entity.setId(key); - 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 deleted file mode 100644 index 81a2e724..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SubjectTest.java +++ /dev/null @@ -1,18 +0,0 @@ -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/SymbolTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SymbolTest.java deleted file mode 100644 index e1bad201..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SymbolTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -import io.github.wulkanowy.data.db.dao.entities.Symbol; -import io.github.wulkanowy.data.db.dao.entities.SymbolDao; - -public class SymbolTest extends AbstractDaoTestLongPk { - - public SymbolTest() { - super(SymbolDao.class); - } - - @Override - protected Symbol createEntity(Long key) { - Symbol entity = new Symbol(); - entity.setId(key); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java deleted file mode 100644 index 190f09b7..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -public class TimetableLessonTest extends AbstractDaoTestLongPk { - - public TimetableLessonTest() { - super(TimetableLessonDao.class); - } - - @Override - protected TimetableLesson createEntity(Long key) { - TimetableLesson entity = new TimetableLesson(); - entity.setId(key); - entity.setEmpty(false); - entity.setDivisionIntoGroups(false); - entity.setPlanning(false); - entity.setRealized(false); - entity.setMovedOrCanceled(false); - entity.setNewMovedInOrChanged(false); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/WeekTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/WeekTest.java deleted file mode 100644 index 86e7a8be..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/WeekTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.test.AbstractDaoTestLongPk; - -public class WeekTest extends AbstractDaoTestLongPk { - - public WeekTest() { - super(WeekDao.class); - } - - @Override - protected Week createEntity(Long key) { - Week entity = new Week(); - entity.setId(key); - return entity; - } - -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt new file mode 100644 index 00000000..f720663b --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.Room +import androidx.room.testing.MigrationTestHelper +import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory +import androidx.test.core.app.ApplicationProvider +import androidx.test.platform.app.InstrumentationRegistry +import io.github.wulkanowy.data.db.AppDatabase +import org.junit.Rule + +abstract class AbstractMigrationTest { + + val dbName = "migration-test" + + @get:Rule + val helper: MigrationTestHelper = MigrationTestHelper( + InstrumentationRegistry.getInstrumentation(), + AppDatabase::class.java.canonicalName, + FrameworkSQLiteOpenHelperFactory() + ) + + fun getMigratedRoomDatabase(): AppDatabase { + val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(), + AppDatabase::class.java, dbName) + .addMigrations( + Migration12(), + Migration13(), + Migration14(), + Migration15() + ) + .build() + // close the database and release any stream resources when the test finishes + helper.closeWhenFinished(database) + return database + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt new file mode 100644 index 00000000..0bbcc427 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt @@ -0,0 +1,133 @@ +package io.github.wulkanowy.data.db.migrations + +import android.content.ContentValues +import android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL +import androidx.sqlite.db.SupportSQLiteDatabase +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class Migration12Test : AbstractMigrationTest() { + + @Test + fun twoNotRelatedStudents() { + helper.createDatabase(dbName, 11).apply { + // user 1 + createStudent(this, 1, true) + createSemester(this, 1, false, 5, 1) + createSemester(this, 1, true, 5, 2) + + // user 2 + createStudent(this, 2, true) + createSemester(this, 2, false, 6, 1) + createSemester(this, 2, true, 6, 2) + close() + } + + helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) + + val db = getMigratedRoomDatabase() + val students = db.studentDao.loadAll().blockingGet() + + assertEquals(2, students.size) + + students[0].run { + assertEquals(1, studentId) + assertEquals(5, classId) + } + + students[1].run { + assertEquals(2, studentId) + assertEquals(6, classId) + } + } + + @Test + fun removeStudentsWithoutClassId() { + helper.createDatabase(dbName, 11).apply { + // user 1 + createStudent(this, 1, true) + createSemester(this, 1, false, 0, 2) + createStudent(this, 2, true) + createSemester(this, 2, true, 1, 2) + close() + } + + helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) + + val db = getMigratedRoomDatabase() + val students = db.studentDao.loadAll().blockingGet() + + assertEquals(1, students.size) + + students[0].run { + assertEquals(2, studentId) + assertEquals(1, classId) + } + } + + @Test + fun ensureThereIsOnlyOneCurrentStudent() { + helper.createDatabase(dbName, 11).apply { + // user 1 + createStudent(this, 1, true) + createSemester(this, 1, true, 5, 2) + createStudent(this, 2, true) + createSemester(this, 2, true, 6, 2) + createStudent(this, 3, true) + createSemester(this, 3, false, 7, 2) + close() + } + + helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) + + val db = getMigratedRoomDatabase() + val students = db.studentDao.loadAll().blockingGet() + + assertEquals(3, students.size) + + students[0].run { + assertEquals(studentId, 1) + assertEquals(false, isCurrent) + } + students[1].run { + assertEquals(studentId, 2) + assertEquals(false, isCurrent) + } + students[2].run { + assertEquals(studentId, 3) + assertEquals(true, isCurrent) + } + } + + private fun createStudent(db: SupportSQLiteDatabase, studentId: Int, isCurrent: Boolean) { + db.insert("Students", CONFLICT_FAIL, ContentValues().apply { + put("endpoint", "https://fakelog.cf") + put("loginType", "STANDARD") + put("email", "jan@fakelog.cf") + put("password", "******") + put("symbol", "Default") + put("student_id", studentId) + put("student_name", "Jan Kowalski") + put("school_id", "000123") + put("school_name", "") + put("is_current", isCurrent) + put("registration_date", "0") + }) + } + + private fun createSemester(db: SupportSQLiteDatabase, studentId: Int, isCurrent: Boolean, classId: Int, diaryId: Int) { + db.insert("Semesters", CONFLICT_FAIL, ContentValues().apply { + put("student_id", studentId) + put("diary_id", diaryId) + put("diary_name", "IA") + put("semester_id", diaryId * 5) + put("semester_name", "1") + put("is_current", isCurrent) + put("class_id", classId) + put("unit_id", "99") + }) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt new file mode 100644 index 00000000..eb9d02a5 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt @@ -0,0 +1,171 @@ +package io.github.wulkanowy.data.db.migrations + +import android.content.ContentValues +import android.database.sqlite.SQLiteDatabase +import androidx.sqlite.db.SupportSQLiteDatabase +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import org.threeten.bp.LocalDate.of +import kotlin.test.assertTrue + +class Migration13Test : AbstractMigrationTest() { + + @Test + fun studentsWithSchoolNameWithClassName() { + helper.createDatabase(dbName, 12).apply { + createStudent(this, 1, "Klasa A - Publiczna szkoła Wulkanowego nr 1 w fakelog.cf", 1, 1) + createStudent(this, 2, "Klasa B - Publiczna szkoła Wulkanowego-fejka nr 1 w fakelog.cf", 2, 1) + createStudent(this, 2, "Klasa C - Publiczna szkoła Wulkanowego-fejka nr 2 w fakelog.cf", 1, 2) + close() + } + + helper.runMigrationsAndValidate(dbName, 13, true, Migration13()) + + val db = getMigratedRoomDatabase() + val students = db.studentDao.loadAll().blockingGet() + + assertEquals(3, students.size) + + students[0].run { + assertEquals(1, studentId) + assertEquals("A", className) + assertEquals("Publiczna szkoła Wulkanowego nr 1 w fakelog.cf", schoolName) + } + + students[1].run { + assertEquals(2, studentId) + assertEquals("B", className) + assertEquals("Publiczna szkoła Wulkanowego-fejka nr 1 w fakelog.cf", schoolName) + } + + students[2].run { + assertEquals(2, studentId) + assertEquals("C", className) + assertEquals("Publiczna szkoła Wulkanowego-fejka nr 2 w fakelog.cf", schoolName) + } + } + + @Test + fun studentsWithSchoolNameWithoutClassName() { + helper.createDatabase(dbName, 12).apply { + createStudent(this, 1, "Publiczna szkoła Wulkanowego nr 1 w fakelog.cf", 1) + createStudent(this, 2, "Publiczna szkoła Wulkanowego-fejka nr 1 w fakelog.cf", 1) + close() + } + + helper.runMigrationsAndValidate(dbName, 13, true, Migration13()) + + val db = getMigratedRoomDatabase() + val students = db.studentDao.loadAll().blockingGet() + + assertEquals(2, students.size) + + students[0].run { + assertEquals(1, studentId) + assertEquals("", className) + assertEquals("Publiczna szkoła Wulkanowego nr 1 w fakelog.cf", schoolName) + } + + students[1].run { + assertEquals(2, studentId) + assertEquals("", className) + assertEquals("Publiczna szkoła Wulkanowego-fejka nr 1 w fakelog.cf", schoolName) + } + } + + @Test + fun markAtLeastAndOnlyOneSemesterAtCurrent() { + helper.createDatabase(dbName, 12).apply { + createStudent(this, 1, "", 5) + createSemester(this, 1, 5, 1, 1, false) + createSemester(this, 1, 5, 2, 1, false) + createSemester(this, 1, 5, 3, 2, false) + createSemester(this, 1, 5, 4, 2, false) + + createStudent(this, 2, "", 5) + createSemester(this, 2, 5, 5, 5, true) + createSemester(this, 2, 5, 6, 5, true) + createSemester(this, 2, 5, 7, 55, true) + createSemester(this, 2, 5, 8, 55, true) + + createStudent(this, 3, "", 5) + createSemester(this, 3, 5, 11, 99, false) + createSemester(this, 3, 5, 12, 99, false) + createSemester(this, 3, 5, 13, 100, false) + createSemester(this, 3, 5, 14, 100, true) + close() + } + + helper.runMigrationsAndValidate(dbName, 13, true, Migration13()) + + val db = getMigratedRoomDatabase() + + val semesters1 = db.semesterDao.loadAll(1, 5).blockingGet() + assertTrue { semesters1.single { it.isCurrent }.isCurrent } + semesters1[0].run { + assertFalse(isCurrent) + assertEquals(1, semesterId) + assertEquals(1, diaryId) + } + semesters1[2].run { + assertFalse(isCurrent) + assertEquals(3, semesterId) + assertEquals(2, diaryId) + } + semesters1[3].run { + assertTrue(isCurrent) + assertEquals(4, semesterId) + assertEquals(2, diaryId) + } + + db.semesterDao.loadAll(2, 5).blockingGet().let { + assertTrue { it.single { it.isCurrent }.isCurrent } + assertEquals(1970, it[0].schoolYear) + assertEquals(of(1970, 1, 1), it[0].end) + assertEquals(of(1970, 1, 1), it[0].start) + assertFalse(it[0].isCurrent) + assertFalse(it[1].isCurrent) + assertFalse(it[2].isCurrent) + assertTrue(it[3].isCurrent) + } + + db.semesterDao.loadAll(2, 5).blockingGet().let { + assertTrue { it.single { it.isCurrent }.isCurrent } + assertFalse(it[0].isCurrent) + assertFalse(it[1].isCurrent) + assertFalse(it[2].isCurrent) + assertTrue(it[3].isCurrent) + } + } + + private fun createStudent(db: SupportSQLiteDatabase, studentId: Int, schoolName: String = "", classId: Int = -1, schoolId: Int = 123) { + db.insert("Students", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { + put("endpoint", "https://fakelog.cf") + put("loginType", "STANDARD") + put("email", "jan@fakelog.cf") + put("password", "******") + put("symbol", "Default") + put("student_id", studentId) + put("class_id", classId) + put("student_name", "Jan Kowalski") + put("school_id", schoolId) + put("school_name", schoolName) + put("is_current", false) + put("registration_date", "0") + }) + } + + private fun createSemester(db: SupportSQLiteDatabase, studentId: Int, classId: Int, semesterId: Int, diaryId: Int, isCurrent: Boolean = false) { + db.insert("Semesters", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { + put("student_id", studentId) + put("diary_id", diaryId) + put("diary_name", "IA") + put("semester_id", semesterId) + put("semester_name", "1") + put("is_current", isCurrent) + put("class_id", classId) + put("unit_id", "99") + }) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestInternetObservingStrategy.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestInternetObservingStrategy.kt new file mode 100644 index 00000000..7dc93c4a --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestInternetObservingStrategy.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.repositories + +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingStrategy +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler +import io.reactivex.Observable +import io.reactivex.Single + +class TestInternetObservingStrategy : InternetObservingStrategy { + + override fun checkInternetConnectivity(host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Single { + return Single.just(true) + } + + override fun observeInternetConnectivity(initialIntervalInMs: Int, intervalInMs: Int, host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Observable { + return Observable.just(true) + } + + override fun getDefaultPingHost() = "localhost" +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt new file mode 100644 index 00000000..69502e74 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.data.repositories.attendance + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.db.entities.Semester +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDate.now +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class AttendanceLocalTest { + + private lateinit var attendanceLocal: AttendanceLocal + + private lateinit var testDb: AppDatabase + + @Before + fun createDb() { + testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build() + attendanceLocal = AttendanceLocal(testDb.attendanceDao) + } + + @After + fun closeDb() { + testDb.close() + } + + @Test + fun saveAndReadTest() { + attendanceLocal.saveAttendance(listOf( + Attendance(1, 2, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false), + Attendance(1, 2, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false), + Attendance(1, 2, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false) + )) + + val attendance = attendanceLocal + .getAttendance(Semester(1, 2, "", 1, 3, 2019, true, now(), now(), 1, 1), + LocalDate.of(2018, 9, 10), + LocalDate.of(2018, 9, 14) + ) + .blockingGet() + assertEquals(2, attendance.size) + assertEquals(attendance[0].date, LocalDate.of(2018, 9, 10)) + assertEquals(attendance[1].date, LocalDate.of(2018, 9, 14)) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt new file mode 100644 index 00000000..356073e8 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt @@ -0,0 +1,57 @@ +package io.github.wulkanowy.data.repositories.completedlessons + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.data.db.entities.Semester +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.threeten.bp.LocalDate +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class CompletedLessonsLocalTest { + + private lateinit var completedLessonsLocal: CompletedLessonsLocal + + private lateinit var testDb: AppDatabase + + @Before + fun createDb() { + testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) + .build() + completedLessonsLocal = CompletedLessonsLocal(testDb.completedLessonsDao) + } + + @After + fun closeDb() { + testDb.close() + } + + @Test + fun saveAndReadTest() { + completedLessonsLocal.saveCompletedLessons(listOf( + getCompletedLesson(LocalDate.of(2018, 9, 10), 1), + getCompletedLesson(LocalDate.of(2018, 9, 14), 2), + getCompletedLesson(LocalDate.of(2018, 9, 17), 3) + )) + + val completed = completedLessonsLocal + .getCompletedLessons(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1), + LocalDate.of(2018, 9, 10), + LocalDate.of(2018, 9, 14) + ) + .blockingGet() + assertEquals(2, completed.size) + assertEquals(completed[0].date, LocalDate.of(2018, 9, 10)) + assertEquals(completed[1].date, LocalDate.of(2018, 9, 14)) + } + + private fun getCompletedLesson(date: LocalDate, number: Int): CompletedLesson { + return CompletedLesson(1, 2, date, number, "", "", "", "", "", "", "") + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt new file mode 100644 index 00000000..fb76306d --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.data.repositories.exam + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.data.db.entities.Semester +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.threeten.bp.LocalDate +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class ExamLocalTest { + + private lateinit var examLocal: ExamLocal + + private lateinit var testDb: AppDatabase + + @Before + fun createDb() { + testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build() + examLocal = ExamLocal(testDb.examsDao) + } + + @After + fun closeDb() { + testDb.close() + } + + @Test + fun saveAndReadTest() { + examLocal.saveExams(listOf( + Exam(1, 2, LocalDate.of(2018, 9, 10), LocalDate.now(), "", "", "", "", "", ""), + Exam(1, 2, LocalDate.of(2018, 9, 14), LocalDate.now(), "", "", "", "", "", ""), + Exam(1, 2, LocalDate.of(2018, 9, 17), LocalDate.now(), "", "", "", "", "", "") + )) + + val exams = examLocal + .getExams(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1), + LocalDate.of(2018, 9, 10), + LocalDate.of(2018, 9, 14) + ) + .blockingGet() + assertEquals(2, exams.size) + assertEquals(exams[0].date, LocalDate.of(2018, 9, 10)) + assertEquals(exams[1].date, LocalDate.of(2018, 9, 14)) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt new file mode 100644 index 00000000..954d0eea --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.data.repositories.grade + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.entities.Semester +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDate.now +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class GradeLocalTest { + + private lateinit var gradeLocal: GradeLocal + + private lateinit var testDb: AppDatabase + + @Before + fun createDb() { + testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) + .build() + gradeLocal = GradeLocal(testDb.gradeDao) + } + + @After + fun closeDb() { + testDb.close() + } + + @Test + fun saveAndReadTest() { + gradeLocal.saveGrades(listOf( + createGradeLocal(5, 3.0, LocalDate.of(2018, 9, 10), "", 1), + createGradeLocal(4, 4.0, LocalDate.of(2019, 2, 27), "", 2), + createGradeLocal(3, 5.0, LocalDate.of(2019, 2, 28), "", 2) + )) + + val semester = Semester(1, 2, "", 2019, 2, 1, true, now(), now(), 1, 1) + + val grades = gradeLocal + .getGrades(semester) + .blockingGet() + + assertEquals(2, grades.size) + assertEquals(grades[0].date, LocalDate.of(2019, 2, 27)) + assertEquals(grades[1].date, LocalDate.of(2019, 2, 28)) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt new file mode 100644 index 00000000..a0acb5a7 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt @@ -0,0 +1,182 @@ +package io.github.wulkanowy.data.repositories.grade + +import android.os.Build.VERSION_CODES.P +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider.getApplicationContext +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SdkSuppress +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.impl.annotations.SpyK +import io.reactivex.Single +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.threeten.bp.LocalDate.of +import org.threeten.bp.LocalDateTime +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue +import io.github.wulkanowy.api.grades.Grade as GradeApi + +@SdkSuppress(minSdkVersion = P) +@RunWith(AndroidJUnit4::class) +class GradeRepositoryTest { + + @SpyK + private var mockApi = Api() + + private val settings = InternetObservingSettings.builder() + .strategy(TestInternetObservingStrategy()) + .build() + + @MockK + private lateinit var semesterMock: Semester + + @MockK + private lateinit var studentMock: Student + + private lateinit var gradeRemote: GradeRemote + + private lateinit var gradeLocal: GradeLocal + + private lateinit var testDb: AppDatabase + + @Before + fun initApi() { + MockKAnnotations.init(this) + testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() + gradeLocal = GradeLocal(testDb.gradeDao) + gradeRemote = GradeRemote(mockApi) + + every { mockApi.diaryId } returns 1 + every { studentMock.registrationDate } returns LocalDateTime.of(2019, 2, 27, 12, 0) + every { semesterMock.studentId } returns 1 + every { semesterMock.semesterId } returns 1 + every { semesterMock.diaryId } returns 1 + } + + @After + fun closeDb() { + testDb.close() + } + + @Test + fun markOlderThanRegisterDateAsRead() { + every { mockApi.getGrades(1) } returns Single.just(listOf( + createGradeApi(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"), + createGradeApi(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"), + createGradeApi(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"), + createGradeApi(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza") + )) + + val grades = GradeRepository(settings, gradeLocal, gradeRemote) + .getGrades(studentMock, semesterMock, true).blockingGet().sortedByDescending { it.date } + + assertFalse { grades[0].isRead } + assertFalse { grades[1].isRead } + assertTrue { grades[2].isRead } + assertTrue { grades[3].isRead } + } + + @Test + fun mitigateOldGradesNotifications() { + gradeLocal.saveGrades(listOf( + createGradeLocal(5, 3.0, of(2019, 2, 25), "Jedna ocena"), + createGradeLocal(4, 4.0, of(2019, 2, 26), "Druga"), + createGradeLocal(3, 5.0, of(2019, 2, 27), "Trzecia") + )) + + every { mockApi.getGrades(1) } returns Single.just(listOf( + createGradeApi(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"), + createGradeApi(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"), + createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"), + createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa") + )) + + val grades = GradeRepository(settings, gradeLocal, gradeRemote) + .getGrades(studentMock, semesterMock, true).blockingGet().sortedByDescending { it.date } + + assertFalse { grades[0].isRead } + assertFalse { grades[1].isRead } + assertTrue { grades[2].isRead } + assertTrue { grades[3].isRead } + } + + @Test + fun subtractLocaleDuplicateGrades() { + gradeLocal.saveGrades(listOf( + createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), + createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), + createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") + )) + + every { mockApi.getGrades(1) } returns Single.just(listOf( + createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), + createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") + )) + + val grades = GradeRepository(settings, gradeLocal, gradeRemote) + .getGrades(studentMock, semesterMock, true).blockingGet() + + assertEquals(2, grades.size) + } + + @Test + fun subtractRemoteDuplicateGrades() { + gradeLocal.saveGrades(listOf( + createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), + createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") + )) + + every { mockApi.getGrades(1) } returns Single.just(listOf( + createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), + createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), + createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") + )) + + val grades = GradeRepository(settings, gradeLocal, gradeRemote) + .getGrades(studentMock, semesterMock, true).blockingGet() + + assertEquals(3, grades.size) + } + + @Test + fun emptyLocal() { + gradeLocal.saveGrades(listOf()) + + every { mockApi.getGrades(1) } returns Single.just(listOf( + createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), + createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), + createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") + )) + + val grades = GradeRepository(settings, gradeLocal, gradeRemote) + .getGrades(studentMock, semesterMock, true).blockingGet() + + assertEquals(3, grades.size) + } + + @Test + fun emptyRemote() { + gradeLocal.saveGrades(listOf( + createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), + createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") + )) + + every { mockApi.getGrades(1) } returns Single.just(listOf()) + + val grades = GradeRepository(settings, gradeLocal, gradeRemote) + .getGrades(studentMock, semesterMock, true).blockingGet() + + assertEquals(0, grades.size) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt new file mode 100644 index 00000000..e0fd05a8 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.data.repositories.grade + +import io.github.wulkanowy.api.toDate +import org.threeten.bp.LocalDate +import io.github.wulkanowy.api.grades.Grade as GradeRemote +import io.github.wulkanowy.data.db.entities.Grade as GradeLocal + +fun createGradeLocal(value: Int, weight: Double, date: LocalDate, desc: String, semesterId: Int = 1): GradeLocal { + return GradeLocal( + semesterId = semesterId, + studentId = 1, + modifier = .0, + teacher = "", + subject = "", + date = date, + color = "", + comment = "", + description = desc, + entry = "", + gradeSymbol = "", + value = value, + weight = "", + weightValue = weight + ) +} + +fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String): GradeRemote { + return GradeRemote().apply { + this.value = value + this.weightValue = weight + this.date = date.toDate() + this.description = desc + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt new file mode 100644 index 00000000..0057a26e --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt @@ -0,0 +1,69 @@ +package io.github.wulkanowy.data.repositories.gradestatistics + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.entities.GradeStatistics +import io.github.wulkanowy.data.db.entities.Semester +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.threeten.bp.LocalDate +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class GradeStatisticsLocalTest { + + private lateinit var gradeStatisticsLocal: GradeStatisticsLocal + + private lateinit var testDb: AppDatabase + + @Before + fun createDb() { + testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) + .build() + gradeStatisticsLocal = GradeStatisticsLocal(testDb.gradeStatistics) + } + + @After + fun closeDb() { + testDb.close() + } + + @Test + fun saveAndRead_subject() { + gradeStatisticsLocal.saveGradesStatistics(listOf( + getGradeStatistics("Matematyka", 2, 1), + getGradeStatistics("Fizyka", 1, 2) + )) + + val stats = gradeStatisticsLocal.getGradesStatistics( + Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false, + "Matematyka" + ).blockingGet() + assertEquals(1, stats.size) + assertEquals(stats[0].subject, "Matematyka") + } + + @Test + fun saveAndRead_all() { + gradeStatisticsLocal.saveGradesStatistics(listOf( + getGradeStatistics("Matematyka", 2, 1), + getGradeStatistics("Chemia", 2, 1), + getGradeStatistics("Fizyka", 1, 2) + )) + + val stats = gradeStatisticsLocal.getGradesStatistics( + Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false, + "Wszystkie" + ).blockingGet() + assertEquals(1, stats.size) + assertEquals(stats[0].subject, "Wszystkie") + } + + private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradeStatistics { + return GradeStatistics(studentId, semesterId, subject, 5, 5, false) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt new file mode 100644 index 00000000..77ddafb9 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt @@ -0,0 +1,47 @@ +package io.github.wulkanowy.data.repositories.luckynumber + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Semester +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.threeten.bp.LocalDate +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class LuckyNumberLocalTest { + + private lateinit var luckyNumberLocal: LuckyNumberLocal + + private lateinit var testDb: AppDatabase + + @Before + fun createDb() { + testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) + .build() + luckyNumberLocal = LuckyNumberLocal(testDb.luckyNumberDao) + } + + @After + fun closeDb() { + testDb.close() + } + + @Test + fun saveAndReadTest() { + luckyNumberLocal.saveLuckyNumber(LuckyNumber(1, LocalDate.of(2019, 1, 20), 14)) + + val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1), + LocalDate.of(2019, 1, 20) + ).blockingGet() + + assertEquals(1, luckyNumber.studentId) + assertEquals(LocalDate.of(2019, 1, 20), luckyNumber.date) + assertEquals(14, luckyNumber.luckyNumber) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt new file mode 100644 index 00000000..6edaccdb --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt @@ -0,0 +1,60 @@ +package io.github.wulkanowy.data.repositories.recipient + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.threeten.bp.LocalDateTime +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class RecipientLocalTest { + + private lateinit var recipientLocal: RecipientLocal + + private lateinit var testDb: AppDatabase + + @Before + fun createDb() { + testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) + .build() + recipientLocal = RecipientLocal(testDb.recipientDao) + } + + @After + fun closeDb() { + testDb.close() + } + + @Test + fun saveAndReadTest() { + recipientLocal.saveRecipients(listOf( + Recipient(1, "2rPracownik", "Kowalski Jan", "Kowalski Jan [KJ] - Pracownik (Fake123456)", 3, 4, 2, "hash"), + Recipient(1, "3rPracownik", "Kowalska Karolina", "Kowalska Karolina [KK] - Pracownik (Fake123456)", 4, 4, 2, "hash"), + Recipient(1, "4rPracownik", "Krupa Stanisław", "Krupa Stanisław [KS] - Uczeń (Fake123456)", 5, 4, 1, "hash") + )) + + val recipients = recipientLocal.getRecipients( + Student("fakelog.cf", "AUTO", "", "", "", 1, "", "", "", "", 1, true, LocalDateTime.now()), + 2, + ReportingUnit(1, 4, "", 0, "", emptyList()) + ).blockingGet() + + assertEquals(2, recipients.size) + assertEquals(1, recipients[0].studentId) + assertEquals("3rPracownik", recipients[1].realId) + assertEquals("Kowalski Jan", recipients[0].name) + assertEquals("Kowalska Karolina [KK] - Pracownik (Fake123456)", recipients[1].realName) + assertEquals(3, recipients[0].loginId) + assertEquals(4, recipients[1].unitId) + assertEquals(2, recipients[0].role) + assertEquals("hash", recipients[1].hash) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt new file mode 100644 index 00000000..cecd8099 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt @@ -0,0 +1,48 @@ +package io.github.wulkanowy.data.repositories.student + +import android.content.Context +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.SharedPrefHelper +import io.github.wulkanowy.data.db.entities.Student +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.threeten.bp.LocalDateTime.now +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class StudentLocalTest { + + private lateinit var studentLocal: StudentLocal + + private lateinit var testDb: AppDatabase + + private lateinit var sharedHelper: SharedPrefHelper + + @Before + fun createDb() { + val context = ApplicationProvider.getApplicationContext() + testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) + .build() + sharedHelper = SharedPrefHelper(context.getSharedPreferences("TEST", Context.MODE_PRIVATE)) + studentLocal = StudentLocal(testDb.studentDao, context) + } + + @After + fun closeDb() { + testDb.close() + } + + @Test + fun saveAndReadTest() { + studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = ""))) + .blockingGet() + + val student = studentLocal.getCurrentStudent(true).blockingGet() + assertEquals("23", student.schoolSymbol) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt new file mode 100644 index 00000000..eea5463d --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt @@ -0,0 +1,45 @@ +package io.github.wulkanowy.data.repositories.timetable + +import io.github.wulkanowy.api.toDate +import io.github.wulkanowy.utils.toDate +import org.threeten.bp.LocalDateTime +import org.threeten.bp.LocalDateTime.now +import io.github.wulkanowy.api.timetable.Timetable as TimetableRemote +import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal + +fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", subject: String = "", teacher: String = ""): TimetableLocal { + return TimetableLocal( + studentId = 1, + diaryId = 2, + number = number, + start = start, + end = now(), + date = start.toLocalDate(), + subject = subject, + subjectOld = "", + group = "", + room = room, + roomOld = "", + teacher = teacher, + teacherOld = "", + info = "", + changes = false, + canceled = false + ) +} + +fun createTimetableRemote(number: Int, start: LocalDateTime, room: String, subject: String = "", teacher: String = ""): TimetableRemote { + return TimetableRemote( + number = number, + start = start.toDate(), + end = start.plusMinutes(45).toDate(), + date = start.toLocalDate().toDate(), + subject = subject, + group = "", + room = room, + teacher = teacher, + info = "", + changes = false, + canceled = false + ) +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt new file mode 100644 index 00000000..fe25e4e9 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.data.repositories.timetable + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.entities.Semester +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDateTime.of +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class TimetableLocalTest { + + private lateinit var timetableDb: TimetableLocal + + private lateinit var testDb: AppDatabase + + @Before + fun createDb() { + testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) + .build() + timetableDb = TimetableLocal(testDb.timetableDao) + } + + @After + fun closeDb() { + testDb.close() + } + + @Test + fun saveAndReadTest() { + timetableDb.saveTimetable(listOf( + createTimetableLocal(1, of(2018, 9, 10, 0, 0, 0)), + createTimetableLocal(1, of(2018, 9, 14, 0, 0, 0)), + createTimetableLocal(1, of(2018, 9, 17, 0, 0, 0)) + )) + + val exams = timetableDb.getTimetable( + Semester(1, 2, "", 1, 1, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1), + LocalDate.of(2018, 9, 10), + LocalDate.of(2018, 9, 14) + ).blockingGet() + + assertEquals(2, exams.size) + assertEquals(exams[0].date, LocalDate.of(2018, 9, 10)) + assertEquals(exams[1].date, LocalDate.of(2018, 9, 14)) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt new file mode 100644 index 00000000..79216d55 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt @@ -0,0 +1,89 @@ +package io.github.wulkanowy.data.repositories.timetable + +import android.os.Build.VERSION_CODES.P +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider.getApplicationContext +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SdkSuppress +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.impl.annotations.SpyK +import io.reactivex.Single +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDateTime.of +import kotlin.test.assertEquals + +@SdkSuppress(minSdkVersion = P) +@RunWith(AndroidJUnit4::class) +class TimetableRepositoryTest { + + @SpyK + private var mockApi = Api() + + private val settings = InternetObservingSettings.builder() + .strategy(TestInternetObservingStrategy()) + .build() + + @MockK + private lateinit var semesterMock: Semester + + private lateinit var timetableRemote: TimetableRemote + + private lateinit var timetableLocal: TimetableLocal + + private lateinit var testDb: AppDatabase + + @Before + fun initApi() { + MockKAnnotations.init(this) + testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() + timetableLocal = TimetableLocal(testDb.timetableDao) + timetableRemote = TimetableRemote(mockApi) + + every { semesterMock.studentId } returns 1 + every { semesterMock.diaryId } returns 2 + } + + @After + fun closeDb() { + testDb.close() + } + + @Test + fun copyDetailsToCompletedFromPrevious() { + timetableLocal.saveTimetable(listOf( + createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda"), + createTimetableLocal(2, of(2019, 3, 5, 8, 50), "321", "Religia"), + createTimetableLocal(3, of(2019, 3, 5, 9, 40), "213", "W-F"), + createTimetableLocal(4, of(2019, 3, 5, 10, 30), "213", "W-F", "Jan Kowalski") + )) + + every { mockApi.getTimetable(any(), any()) } returns Single.just(listOf( + createTimetableRemote(1, of(2019, 3, 5, 8, 0), "", "Przyroda"), + createTimetableRemote(2, of(2019, 3, 5, 8, 50), "", "Religia"), + createTimetableRemote(3, of(2019, 3, 5, 9, 40), "", "W-F"), + createTimetableRemote(4, of(2019, 3, 5, 10, 30), "", "W-F") + )) + + val lessons = TimetableRepository(settings, timetableLocal, timetableRemote) + .getTimetable(semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true) + .blockingGet() + + assertEquals(4, lessons.size) + assertEquals("123", lessons[0].room) + assertEquals("321", lessons[1].room) + assertEquals("213", lessons[2].room) + + assertEquals("", lessons[3].teacher) + } +} 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 deleted file mode 100644 index 2fd1904b..00000000 --- a/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.utils.security; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SdkSuppress; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class 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 new file mode 100644 index 00000000..0c47e6bb --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt @@ -0,0 +1,47 @@ +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 new file mode 100644 index 00000000..e9303986 --- /dev/null +++ b/app/src/debug/google-services.json @@ -0,0 +1,42 @@ +{ + "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 new file mode 100644 index 00000000..736f7906 Binary files /dev/null and b/app/src/debug/google-services.json.gpg differ diff --git a/app/src/debug/res/drawable/ic_launcher_foreground.xml b/app/src/debug/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 00000000..6be79909 --- /dev/null +++ b/app/src/debug/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..7353dbd1 --- /dev/null +++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..7353dbd1 --- /dev/null +++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher.png b/app/src/debug/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..5b688d7c Binary files /dev/null and b/app/src/debug/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..81e723ec Binary files /dev/null and b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher.png b/app/src/debug/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..48b13240 Binary files /dev/null and b/app/src/debug/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..394b5707 Binary files /dev/null and b/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..ff8bfa3e Binary files /dev/null and b/app/src/debug/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000..365b4d66 Binary files /dev/null and b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..96be1ed4 Binary files /dev/null and b/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..463c089b Binary files /dev/null and b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..57c7416f Binary files /dev/null and b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..53d6f5bb Binary files /dev/null and b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/values/ic_launcher_background.xml b/app/src/debug/res/values/ic_launcher_background.xml new file mode 100644 index 00000000..9646c0b4 --- /dev/null +++ b/app/src/debug/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #D32F2F + \ No newline at end of file diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt new file mode 100644 index 00000000..33ef5a58 --- /dev/null +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt @@ -0,0 +1,17 @@ +@file:Suppress("UNUSED_PARAMETER") + +package io.github.wulkanowy.utils + +import android.content.Context +import timber.log.Timber + +class CrashlyticsTree : Timber.Tree() { + + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + // do nothing + } +} + +fun initCrashlytics(context: Context) { + // do nothing +} diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt new file mode 100644 index 00000000..0b1274f1 --- /dev/null +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.utils + +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class FirebaseAnalyticsHelper @Inject constructor() { + + @Suppress("UNUSED_PARAMETER") + fun logEvent(name: String, vararg params: Pair) { + // do nothing + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dd982626..2a214b8b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,12 +1,15 @@ + + + + android:supportsRtl="false" + android:theme="@style/WulkanowyTheme" + android:usesCleartextTraffic="true" + tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"> + android:name=".ui.modules.splash.SplashActivity" + android:screenOrientation="portrait" + android:theme="@style/WulkanowyTheme.SplashScreen"> - + android:label="@string/main_title" + android:theme="@style/WulkanowyTheme.NoActionBar" /> + android:name=".ui.modules.message.send.SendMessageActivity" + android:configChanges="orientation|screenSize" + android:label="@string/send_message_title" + android:theme="@style/WulkanowyTheme.NoActionBar" /> + android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity" + android:excludeFromRecents="true" + android:noHistory="true" + android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher"> + + + + + + + + + - - - - - - + + android:resource="@xml/provider_widget_timetable" /> + + + + + + + + + + android:value="${fabric_api_key}" /> + - diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png deleted file mode 100644 index 7cad5c2f..00000000 Binary files a/app/src/main/ic_launcher-web.png and /dev/null differ diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.java b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.java deleted file mode 100644 index d3570512..00000000 --- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.java +++ /dev/null @@ -1,74 +0,0 @@ -package io.github.wulkanowy; - -import com.crashlytics.android.Crashlytics; -import com.crashlytics.android.answers.Answers; -import com.crashlytics.android.core.CrashlyticsCore; -import com.jakewharton.threetenabp.AndroidThreeTen; - -import org.greenrobot.greendao.query.QueryBuilder; - -import javax.inject.Inject; - -import dagger.android.AndroidInjector; -import dagger.android.support.DaggerApplication; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import io.fabric.sdk.android.Fabric; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.di.DaggerAppComponent; -import io.github.wulkanowy.utils.FabricUtils; -import io.github.wulkanowy.utils.LoggerUtils; -import timber.log.Timber; - -public class WulkanowyApp extends DaggerApplication { - - @Inject - RepositoryContract repository; - - @Override - public void onCreate() { - super.onCreate(); - AndroidThreeTen.init(this); - - if (BuildConfig.DEBUG) { - enableDebugLog(); - } - initializeFabric(); - initializeUserSession(); - } - - private void initializeUserSession() { - if (repository.getSharedRepo().isUserLoggedIn()) { - try { - repository.getSyncRepo().initLastUser(); - FabricUtils.logLogin("Open app", true); - } catch (Exception e) { - FabricUtils.logLogin("Open app", false); - Timber.e(e, "An error occurred when the application was started"); - } - } - } - - private void enableDebugLog() { - QueryBuilder.LOG_VALUES = true; - FlexibleAdapter.enableLogs(eu.davidea.flexibleadapter.utils.Log.Level.DEBUG); - Timber.plant(new LoggerUtils.DebugLogTree()); - } - - private void initializeFabric() { - Fabric.with(new Fabric.Builder(this) - .kits( - new Crashlytics.Builder() - .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()) - .build(), - new Answers() - ) - .debuggable(BuildConfig.DEBUG) - .build()); - Timber.plant(new LoggerUtils.CrashlyticsTree()); - } - - @Override - protected AndroidInjector applicationInjector() { - return DaggerAppComponent.builder().create(this); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt new file mode 100644 index 00000000..35a0e8da --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt @@ -0,0 +1,64 @@ +package io.github.wulkanowy + +import android.content.Context +import androidx.multidex.MultiDex +import androidx.work.Configuration +import androidx.work.WorkManager +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.github.wulkanowy.BuildConfig.DEBUG +import io.github.wulkanowy.di.DaggerAppComponent +import io.github.wulkanowy.services.sync.SyncWorkerFactory +import io.github.wulkanowy.utils.ActivityLifecycleLogger +import io.github.wulkanowy.utils.CrashlyticsTree +import io.github.wulkanowy.utils.DebugLogTree +import io.github.wulkanowy.utils.initCrashlytics +import io.reactivex.exceptions.UndeliverableException +import io.reactivex.plugins.RxJavaPlugins +import timber.log.Timber +import java.io.IOException +import javax.inject.Inject + +class WulkanowyApp : DaggerApplication() { + + @Inject + lateinit var workerFactory: SyncWorkerFactory + + override fun attachBaseContext(base: Context?) { + super.attachBaseContext(base) + MultiDex.install(this) + } + + override fun onCreate() { + super.onCreate() + AndroidThreeTen.init(this) + WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(workerFactory).build()) + RxJavaPlugins.setErrorHandler(::onError) + + initCrashlytics(applicationContext) + initLogging() + } + + private fun initLogging() { + if (DEBUG) { + Timber.plant(DebugLogTree()) + FlexibleAdapter.enableLogs(Log.Level.DEBUG) + } else { + Timber.plant(CrashlyticsTree()) + } + registerActivityLifecycleCallbacks(ActivityLifecycleLogger()) + } + + private fun onError(error: Throwable) { + if (error is UndeliverableException && error.cause is IOException || error.cause is InterruptedException) { + Timber.e(error.cause, "An undeliverable error occurred") + } else throw error + } + + override fun applicationInjector(): AndroidInjector { + return DaggerAppComponent.factory().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 new file mode 100644 index 00000000..b6eee316 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt @@ -0,0 +1,35 @@ +package io.github.wulkanowy.data + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.Student +import java.net.URL +import javax.inject.Inject + +class ApiHelper @Inject constructor(private val api: Api) { + + fun initApi(student: Student) { + api.apply { + email = student.email + password = student.password + symbol = student.symbol + schoolSymbol = student.schoolSymbol + studentId = student.studentId + classId = student.classId + host = URL(student.endpoint).run { host + ":$port".removeSuffix(":-1") } + ssl = student.endpoint.startsWith("https") + loginType = Api.LoginType.valueOf(student.loginType) + useNewStudent = true + } + } + + fun initApi(email: String, password: String, symbol: String, endpoint: String) { + api.apply { + this.email = email + this.password = password + this.symbol = symbol + host = URL(endpoint).run { host + ":$port".removeSuffix(":-1") } + ssl = endpoint.startsWith("https") + useNewStudent = true + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/Repository.java b/app/src/main/java/io/github/wulkanowy/data/Repository.java deleted file mode 100644 index ac6a8b42..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/Repository.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.github.wulkanowy.data; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.data.db.dao.DbContract; -import io.github.wulkanowy.data.db.resources.ResourcesContract; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.data.sync.SyncContract; - -@Singleton -public class Repository implements RepositoryContract { - - private final DbContract database; - - private final ResourcesContract resources; - - private final SharedPrefContract sharedPref; - - private final SyncContract synchronization; - - @Inject - Repository(DbContract database, ResourcesContract resources, SharedPrefContract sharedPref, - SyncContract synchronization) { - this.database = database; - this.resources = resources; - this.sharedPref = sharedPref; - this.synchronization = synchronization; - } - - @Override - public SharedPrefContract getSharedRepo() { - return sharedPref; - } - - @Override - public ResourcesContract getResRepo() { - return resources; - } - - @Override - public DbContract getDbRepo() { - return database; - } - - @Override - public SyncContract getSyncRepo() { - return synchronization; - } - - @Override - public void cleanAllData() { - sharedPref.cleanSharedPref(); - database.recreateDatabase(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java b/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java deleted file mode 100644 index e4dbd26b..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.wulkanowy.data; - -import javax.inject.Singleton; - -import io.github.wulkanowy.data.db.dao.DbContract; -import io.github.wulkanowy.data.db.resources.ResourcesContract; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.data.sync.SyncContract; - -@Singleton -public interface RepositoryContract { - - SharedPrefContract getSharedRepo(); - - ResourcesContract getResRepo(); - - DbContract getDbRepo(); - - SyncContract getSyncRepo(); - - void cleanAllData(); -} diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt new file mode 100644 index 00000000..97da6bb8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt @@ -0,0 +1,139 @@ +package io.github.wulkanowy.data + +import android.content.Context +import android.content.SharedPreferences +import android.content.res.Resources +import androidx.preference.PreferenceManager +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.WalledGardenInternetObservingStrategy +import com.readystatesoftware.chuck.api.ChuckCollector +import com.readystatesoftware.chuck.api.ChuckInterceptor +import com.readystatesoftware.chuck.api.RetentionManager +import dagger.Module +import dagger.Provides +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import okhttp3.logging.HttpLoggingInterceptor +import okhttp3.logging.HttpLoggingInterceptor.Level.BASIC +import okhttp3.logging.HttpLoggingInterceptor.Level.NONE +import timber.log.Timber +import javax.inject.Singleton + +@Module +internal class RepositoryModule { + + @Singleton + @Provides + fun provideInternetObservingSettings(): InternetObservingSettings { + return InternetObservingSettings.builder() + .strategy(WalledGardenInternetObservingStrategy()) + .build() + } + + @Singleton + @Provides + fun provideApi(chuckCollector: ChuckCollector, context: Context): Api { + return Api().apply { + logLevel = NONE + androidVersion = android.os.Build.VERSION.RELEASE + buildTag = android.os.Build.MODEL + setInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { Timber.d(it) }).setLevel(BASIC)) + + // for debug only + setInterceptor(ChuckInterceptor(context, chuckCollector).maxContentLength(250000L), true, 0) + } + } + + @Singleton + @Provides + fun provideChuckCollector(context: Context, prefRepository: PreferencesRepository): ChuckCollector { + return ChuckCollector(context) + .showNotification(prefRepository.isDebugNotificationEnable) + .retentionManager(RetentionManager(context, ChuckCollector.Period.ONE_HOUR)) + } + + @Singleton + @Provides + fun provideDatabase(context: Context) = AppDatabase.newInstance(context) + + @Singleton + @Provides + fun provideResources(context: Context): Resources = context.resources + + @Singleton + @Provides + fun provideSharedPref(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + + @Singleton + @Provides + fun provideStudentDao(database: AppDatabase) = database.studentDao + + @Singleton + @Provides + fun provideSemesterDao(database: AppDatabase) = database.semesterDao + + @Singleton + @Provides + fun provideGradeDao(database: AppDatabase) = database.gradeDao + + @Singleton + @Provides + fun provideGradeSummaryDao(database: AppDatabase) = database.gradeSummaryDao + + @Singleton + @Provides + fun provideGradeStatisticsDao(database: AppDatabase) = database.gradeStatistics + + @Singleton + @Provides + fun provideMessagesDao(database: AppDatabase) = database.messagesDao + + @Singleton + @Provides + fun provideExamDao(database: AppDatabase) = database.examsDao + + @Singleton + @Provides + fun provideAttendanceDao(database: AppDatabase) = database.attendanceDao + + @Singleton + @Provides + fun provideAttendanceSummaryDao(database: AppDatabase) = database.attendanceSummaryDao + + @Singleton + @Provides + fun provideTimetableDao(database: AppDatabase) = database.timetableDao + + @Singleton + @Provides + fun provideNoteDao(database: AppDatabase) = database.noteDao + + @Singleton + @Provides + fun provideHomeworkDao(database: AppDatabase) = database.homeworkDao + + @Singleton + @Provides + fun provideSubjectDao(database: AppDatabase) = database.subjectDao + + @Singleton + @Provides + fun provideLuckyNumberDao(database: AppDatabase) = database.luckyNumberDao + + @Singleton + @Provides + fun provideCompletedLessonsDao(database: AppDatabase) = database.completedLessonsDao + + @Singleton + @Provides + fun provideReportingUnitDao(database: AppDatabase) = database.reportingUnitDao + + @Singleton + @Provides + fun provideRecipientDao(database: AppDatabase) = database.recipientDao + + @Singleton + @Provides + fun provideMobileDevicesDao(database: AppDatabase) = database.mobileDeviceDao +} 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 new file mode 100644 index 00000000..a97a3042 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -0,0 +1,152 @@ +package io.github.wulkanowy.data.db + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.RoomDatabase.JournalMode.TRUNCATE +import androidx.room.TypeConverters +import io.github.wulkanowy.data.db.dao.AttendanceDao +import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao +import io.github.wulkanowy.data.db.dao.CompletedLessonsDao +import io.github.wulkanowy.data.db.dao.ExamDao +import io.github.wulkanowy.data.db.dao.GradeDao +import io.github.wulkanowy.data.db.dao.GradeStatisticsDao +import io.github.wulkanowy.data.db.dao.GradeSummaryDao +import io.github.wulkanowy.data.db.dao.HomeworkDao +import io.github.wulkanowy.data.db.dao.LuckyNumberDao +import io.github.wulkanowy.data.db.dao.MessagesDao +import io.github.wulkanowy.data.db.dao.MobileDeviceDao +import io.github.wulkanowy.data.db.dao.NoteDao +import io.github.wulkanowy.data.db.dao.RecipientDao +import io.github.wulkanowy.data.db.dao.ReportingUnitDao +import io.github.wulkanowy.data.db.dao.SemesterDao +import io.github.wulkanowy.data.db.dao.StudentDao +import io.github.wulkanowy.data.db.dao.SubjectDao +import io.github.wulkanowy.data.db.dao.TimetableDao +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.GradeStatistics +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MobileDevice +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.Subject +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.db.migrations.Migration10 +import io.github.wulkanowy.data.db.migrations.Migration11 +import io.github.wulkanowy.data.db.migrations.Migration12 +import io.github.wulkanowy.data.db.migrations.Migration13 +import io.github.wulkanowy.data.db.migrations.Migration14 +import io.github.wulkanowy.data.db.migrations.Migration15 +import io.github.wulkanowy.data.db.migrations.Migration2 +import io.github.wulkanowy.data.db.migrations.Migration3 +import io.github.wulkanowy.data.db.migrations.Migration4 +import io.github.wulkanowy.data.db.migrations.Migration5 +import io.github.wulkanowy.data.db.migrations.Migration6 +import io.github.wulkanowy.data.db.migrations.Migration7 +import io.github.wulkanowy.data.db.migrations.Migration8 +import io.github.wulkanowy.data.db.migrations.Migration9 +import javax.inject.Singleton + +@Singleton +@Database( + entities = [ + Student::class, + Semester::class, + Exam::class, + Timetable::class, + Attendance::class, + AttendanceSummary::class, + Grade::class, + GradeSummary::class, + GradeStatistics::class, + Message::class, + Note::class, + Homework::class, + Subject::class, + LuckyNumber::class, + CompletedLesson::class, + ReportingUnit::class, + Recipient::class, + MobileDevice::class + ], + version = AppDatabase.VERSION_SCHEMA, + exportSchema = true +) +@TypeConverters(Converters::class) +abstract class AppDatabase : RoomDatabase() { + + companion object { + const val VERSION_SCHEMA = 15 + + fun newInstance(context: Context): AppDatabase { + return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database") + .setJournalMode(TRUNCATE) + .fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1) + .fallbackToDestructiveMigrationOnDowngrade() + .addMigrations( + Migration2(), + Migration3(), + Migration4(), + Migration5(), + Migration6(), + Migration7(), + Migration8(), + Migration9(), + Migration10(), + Migration11(), + Migration12(), + Migration13(), + Migration14(), + Migration15() + ) + .build() + } + } + + abstract val studentDao: StudentDao + + abstract val semesterDao: SemesterDao + + abstract val examsDao: ExamDao + + abstract val timetableDao: TimetableDao + + abstract val attendanceDao: AttendanceDao + + abstract val attendanceSummaryDao: AttendanceSummaryDao + + abstract val gradeDao: GradeDao + + abstract val gradeSummaryDao: GradeSummaryDao + + abstract val gradeStatistics: GradeStatisticsDao + + abstract val messagesDao: MessagesDao + + abstract val noteDao: NoteDao + + abstract val homeworkDao: HomeworkDao + + abstract val subjectDao: SubjectDao + + abstract val luckyNumberDao: LuckyNumberDao + + abstract val completedLessonsDao: CompletedLessonsDao + + abstract val reportingUnitDao: ReportingUnitDao + + abstract val recipientDao: RecipientDao + + abstract val mobileDeviceDao: MobileDeviceDao +} 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 new file mode 100644 index 00000000..73a04d23 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt @@ -0,0 +1,51 @@ +package io.github.wulkanowy.data.db + +import androidx.room.TypeConverter +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import org.threeten.bp.DateTimeUtils +import org.threeten.bp.Instant +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDateTime +import org.threeten.bp.Month +import org.threeten.bp.ZoneOffset +import java.util.Date + +class Converters { + + @TypeConverter + fun timestampToDate(value: Long?): LocalDate? = value?.run { + DateTimeUtils.toInstant(Date(value)).atZone(ZoneOffset.UTC).toLocalDate() + } + + @TypeConverter + fun dateToTimestamp(date: LocalDate?): Long? { + return date?.atStartOfDay()?.toInstant(ZoneOffset.UTC)?.toEpochMilli() + } + + @TypeConverter + fun timestampToTime(value: Long?): LocalDateTime? = value?.let { + LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.UTC) + } + + @TypeConverter + fun timeToTimestamp(date: LocalDateTime?): Long? { + return date?.atZone(ZoneOffset.UTC)?.toInstant()?.toEpochMilli() + } + + @TypeConverter + fun monthToInt(month: Month?) = month?.value + + @TypeConverter + fun intToMonth(value: Int?) = value?.let { Month.of(it) } + + @TypeConverter + fun intListToGson(list: List): String { + return Gson().toJson(list) + } + + @TypeConverter + fun gsonToIntList(value: String): List { + return Gson().fromJson(value, object : TypeToken>() {}.type) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt new file mode 100644 index 00000000..74f9fa65 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.data.db + +import android.annotation.SuppressLint +import android.content.SharedPreferences +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +@SuppressLint("ApplySharedPref") +class SharedPrefHelper @Inject constructor(private val sharedPref: SharedPreferences) { + + 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) = sharedPref.getLong(key, defaultValue) + + fun delete(key: String) { + sharedPref.edit().remove(key).apply() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt new file mode 100644 index 00000000..d3c4f146 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt @@ -0,0 +1,24 @@ +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 new file mode 100644 index 00000000..a7413de5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.reactivex.Maybe + +@Dao +interface AttendanceSummaryDao { + + @Insert + fun insertAll(exams: List): List + + @Delete + fun deleteAll(exams: List) + + @Query("SELECT * FROM AttendanceSummary WHERE diary_id = :diaryId AND student_id = :studentId AND subject_id = :subjectId") + fun loadAll(diaryId: Int, studentId: Int, subjectId: Int): Maybe> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt new file mode 100644 index 00000000..6816ceaa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt @@ -0,0 +1,24 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.reactivex.Maybe +import org.threeten.bp.LocalDate +import javax.inject.Singleton + +@Singleton +@Dao +interface CompletedLessonsDao { + + @Insert + fun insertAll(exams: List) + + @Delete + fun deleteAll(exams: List) + + @Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") + fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbContract.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbContract.java deleted file mode 100644 index 22eaa944..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbContract.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.data.db.dao; - -import java.util.List; - -import io.github.wulkanowy.data.db.dao.entities.Grade; -import io.github.wulkanowy.data.db.dao.entities.Subject; -import io.github.wulkanowy.data.db.dao.entities.Symbol; -import io.github.wulkanowy.data.db.dao.entities.Week; - -public interface DbContract { - - Week getWeek(String date); - - Week getWeek(long diaryId, String date); - - List getSubjectList(int semesterName); - - List getNewGrades(int semesterName); - - long getCurrentSchoolId(); - - long getCurrentStudentId(); - - long getCurrentSymbolId(); - - Symbol getCurrentSymbol(); - - long getCurrentDiaryId(); - - long getSemesterId(int name); - - long getCurrentSemesterId(); - - int getCurrentSemesterName(); - - void recreateDatabase(); -} 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 deleted file mode 100644 index 33b19311..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java +++ /dev/null @@ -1,102 +0,0 @@ -package io.github.wulkanowy.data.db.dao; - -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; - -import org.greenrobot.greendao.database.Database; -import org.greenrobot.greendao.database.StandardDatabase; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.data.db.dao.entities.DaoMaster; -import io.github.wulkanowy.data.db.dao.migrations.Migration23; -import io.github.wulkanowy.data.db.dao.migrations.Migration26; -import io.github.wulkanowy.data.db.dao.migrations.Migration27; -import io.github.wulkanowy.data.db.dao.migrations.Migration28; -import io.github.wulkanowy.data.db.dao.migrations.Migration29; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import timber.log.Timber; - -@Singleton -public class DbHelper extends DaoMaster.OpenHelper { - - private final SharedPrefContract sharedPref; - - private final Vulcan vulcan; - - @Inject - DbHelper(Context context, @Named("dbName") String dbName, - SharedPrefContract sharedPref, Vulcan vulcan) { - super(context, dbName); - this.sharedPref = sharedPref; - this.vulcan = vulcan; - } - - @Override - public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { - Timber.i("Cleaning user data oldVersion=%s newVersion=%s", oldVersion, newVersion); - Database database = new StandardDatabase(db); - recreateDatabase(database); - } - - @Override - public void onUpgrade(Database db, int oldVersion, int newVersion) { - List migrations = getMigrations(); - - // Only run migrations past the old version - for (Migration migration : migrations) { - if (oldVersion < migration.getVersion()) { - try { - Timber.i("Applying migration to db schema v%s...", migration.getVersion()); - migration.runMigration(db, sharedPref, vulcan); - Timber.i("Migration %s complete", migration.getVersion()); - } catch (Exception e) { - Timber.e(e, "Failed to apply migration"); - recreateDatabase(db); - break; - } - } - } - } - - private void recreateDatabase(Database db) { - Timber.i("Database is recreating..."); - sharedPref.setCurrentUserId(0); - DaoMaster.dropAllTables(db, true); - onCreate(db); - } - - private List getMigrations() { - List migrations = new ArrayList<>(); - migrations.add(new Migration23()); - migrations.add(new Migration26()); - migrations.add(new Migration27()); - migrations.add(new Migration28()); - migrations.add(new Migration29()); - - // Sorting just to be safe, in case other people add migrations in the wrong order. - Comparator migrationComparator = new Comparator() { - @Override - public int compare(Migration m1, Migration m2) { - return m1.getVersion().compareTo(m2.getVersion()); - } - }; - Collections.sort(migrations, migrationComparator); - - return migrations; - } - - public interface Migration { - Integer getVersion(); - - void runMigration(Database db, SharedPrefContract sharedPref, Vulcan vulcan) throws Exception; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbRepository.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbRepository.java deleted file mode 100644 index d869bfe9..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbRepository.java +++ /dev/null @@ -1,132 +0,0 @@ -package io.github.wulkanowy.data.db.dao; - -import org.greenrobot.greendao.database.Database; - -import java.util.List; - -import javax.inject.Inject; - -import io.github.wulkanowy.data.db.dao.entities.DaoMaster; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.dao.entities.DiaryDao; -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.SchoolDao; -import io.github.wulkanowy.data.db.dao.entities.Semester; -import io.github.wulkanowy.data.db.dao.entities.SemesterDao; -import io.github.wulkanowy.data.db.dao.entities.StudentDao; -import io.github.wulkanowy.data.db.dao.entities.Subject; -import io.github.wulkanowy.data.db.dao.entities.Symbol; -import io.github.wulkanowy.data.db.dao.entities.SymbolDao; -import io.github.wulkanowy.data.db.dao.entities.Week; -import io.github.wulkanowy.data.db.dao.entities.WeekDao; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; - -public class DbRepository implements DbContract { - - private final DaoSession daoSession; - - private final SharedPrefContract sharedPref; - - @Inject - DbRepository(DaoSession daoSession, SharedPrefContract sharedPrefContract) { - this.daoSession = daoSession; - this.sharedPref = sharedPrefContract; - } - - - @Override - public Week getWeek(String date) { - return getWeek(getCurrentDiaryId(), date); - } - - @Override - public Week getWeek(long diaryId, String date) { - return daoSession.getWeekDao().queryBuilder().where( - WeekDao.Properties.StartDayDate.eq(date), - WeekDao.Properties.DiaryId.eq(diaryId) - ).unique(); - } - - @Override - public List getSubjectList(int semesterName) { - return daoSession.getSemesterDao().load(getSemesterId(semesterName)).getSubjectList(); - } - - @Override - public List getNewGrades(int semesterName) { - return daoSession.getGradeDao().queryBuilder().where( - GradeDao.Properties.IsNew.eq(1), - GradeDao.Properties.SemesterId.eq(getSemesterId(semesterName)) - ).list(); - } - - @Override - public Symbol getCurrentSymbol() { - return daoSession.getSymbolDao().queryBuilder().where( - SymbolDao.Properties.UserId.eq(sharedPref.getCurrentUserId()) - ).unique(); - } - - @Override - public long getCurrentSymbolId() { - return getCurrentSymbol().getId(); - } - - @Override - public long getCurrentSchoolId() { - return daoSession.getSchoolDao().queryBuilder().where( - SchoolDao.Properties.SymbolId.eq(getCurrentSymbolId()), - SchoolDao.Properties.Current.eq(true) - ).unique().getId(); - } - - @Override - public long getCurrentStudentId() { - return daoSession.getStudentDao().queryBuilder().where( - StudentDao.Properties.SchoolId.eq(getCurrentSchoolId()), - StudentDao.Properties.Current.eq(true) - ).unique().getId(); - } - - @Override - public long getCurrentDiaryId() { - return daoSession.getDiaryDao().queryBuilder().where( - DiaryDao.Properties.StudentId.eq(getCurrentStudentId()), - DiaryDao.Properties.Current.eq(true) - ).unique().getId(); - } - - @Override - public long getSemesterId(int name) { - return daoSession.getSemesterDao().queryBuilder().where( - SemesterDao.Properties.DiaryId.eq(getCurrentDiaryId()), - SemesterDao.Properties.Name.eq(String.valueOf(name)) - ).unique().getId(); - } - - @Override - public long getCurrentSemesterId() { - return getCurrentSemester().getId(); - } - - @Override - public int getCurrentSemesterName() { - return Integer.valueOf(getCurrentSemester().getName()); - } - - private Semester getCurrentSemester() { - return daoSession.getSemesterDao().queryBuilder().where( - SemesterDao.Properties.DiaryId.eq(getCurrentDiaryId()), - SemesterDao.Properties.Current.eq(true) - ).unique(); - } - - @Override - public void recreateDatabase() { - Database database = daoSession.getDatabase(); - - DaoMaster.dropAllTables(database, true); - DaoMaster.createAllTables(database, true); - } -} 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 new file mode 100644 index 00000000..06cd5613 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt @@ -0,0 +1,24 @@ +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 new file mode 100644 index 00000000..0bd210b0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt @@ -0,0 +1,28 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import io.github.wulkanowy.data.db.entities.Grade +import io.reactivex.Maybe +import javax.inject.Singleton + +@Singleton +@Dao +interface GradeDao { + + @Insert + fun insertAll(grades: List) + + @Update + fun updateAll(grade: List) + + @Delete + fun deleteAll(grades: List) + + @Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId") + fun loadAll(semesterId: Int, studentId: Int): Maybe> + +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt new file mode 100644 index 00000000..338c369f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.GradeStatistics +import io.reactivex.Maybe +import javax.inject.Singleton + +@Singleton +@Dao +interface GradeStatisticsDao { + + @Insert + fun insertAll(gradesStatistics: List) + + @Delete + fun deleteAll(gradesStatistics: List) + + @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester") + fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): Maybe> + + @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND is_semester = :isSemester") + fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): Maybe> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt new file mode 100644 index 00000000..3f2e87bd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.reactivex.Maybe +import javax.inject.Singleton + +@Singleton +@Dao +interface GradeSummaryDao { + + @Insert + fun insertAll(gradesSummary: List) + + @Delete + fun deleteAll(gradesSummary: List) + + @Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId") + fun loadAll(semesterId: Int, studentId: Int): Maybe> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt new file mode 100644 index 00000000..253bdb11 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt @@ -0,0 +1,24 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Homework +import io.reactivex.Maybe +import org.threeten.bp.LocalDate +import javax.inject.Singleton + +@Singleton +@Dao +interface HomeworkDao { + + @Insert + fun insertAll(homework: List) + + @Delete + fun deleteAll(homework: List) + + @Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date >= :from AND date <= :end") + fun loadAll(semesterId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt new file mode 100644 index 00000000..afd7905c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.reactivex.Maybe +import org.threeten.bp.LocalDate +import javax.inject.Singleton + +@Singleton +@Dao +interface LuckyNumberDao { + + @Insert + fun insert(luckyNumber: LuckyNumber) + + @Update + fun update(luckyNumber: LuckyNumber) + + @Delete + fun delete(luckyNumber: LuckyNumber) + + @Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date") + fun load(studentId: Int, date: LocalDate): Maybe + +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt new file mode 100644 index 00000000..4f72c6c9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import io.github.wulkanowy.data.db.entities.Message +import io.reactivex.Maybe + +@Dao +interface MessagesDao { + + @Insert + fun insertAll(messages: List) + + @Delete + fun deleteAll(messages: List) + + @Update + fun updateAll(messages: List) + + @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC") + fun loadAll(studentId: Int, folder: Int): Maybe> + + @Query("SELECT * FROM Messages WHERE id = :id") + fun load(id: Long): Maybe + + @Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC") + fun loadDeleted(studentId: Int): Maybe> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt new file mode 100644 index 00000000..d6b97f6a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt @@ -0,0 +1,21 @@ +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.MobileDevice +import io.reactivex.Maybe + +@Dao +interface MobileDeviceDao { + + @Insert + fun insertAll(devices: List) + + @Delete + fun deleteAll(devices: List) + + @Query("SELECT * FROM MobileDevices WHERE student_id = :studentId ORDER BY date DESC") + fun loadAll(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 new file mode 100644 index 00000000..867e06a2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt @@ -0,0 +1,28 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import io.github.wulkanowy.data.db.entities.Note +import io.reactivex.Maybe +import javax.inject.Singleton + +@Singleton +@Dao +interface NoteDao { + + @Insert + fun insertAll(notes: List) + + @Update + fun updateAll(notes: List) + + @Delete + fun deleteAll(notes: List) + + @Query("SELECT * FROM Notes WHERE student_id = :studentId") + fun loadAll(studentId: Int): Maybe> + +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt new file mode 100644 index 00000000..7c5fd6ca --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Recipient +import io.reactivex.Maybe +import javax.inject.Singleton + +@Singleton +@Dao +interface RecipientDao { + + @Insert + fun insertAll(messages: List) + + @Delete + fun deleteAll(messages: List) + + @Query("SELECT * FROM Recipients WHERE student_id = :studentId AND role = :role AND unit_id = :unitId") + fun load(studentId: Int, role: Int, unitId: Int): Maybe> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt new file mode 100644 index 00000000..1898390a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.reactivex.Maybe +import javax.inject.Singleton + +@Singleton +@Dao +interface ReportingUnitDao { + + @Insert + fun insertAll(reportingUnits: List) + + @Delete + fun deleteAll(reportingUnits: List) + + @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId") + fun load(studentId: Int): Maybe> + + @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId") + fun loadOne(studentId: Int, unitId: Int): Maybe +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt new file mode 100644 index 00000000..01841fb6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Maybe +import javax.inject.Singleton + +@Singleton +@Dao +interface SemesterDao { + + @Insert + fun insertAll(semester: List) + + @Delete + fun deleteAll(semester: List) + + @Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId") + fun loadAll(studentId: Int, classId: Int): Maybe> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt new file mode 100644 index 00000000..57bf25fb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy.ABORT +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Student +import io.reactivex.Maybe +import javax.inject.Singleton + +@Singleton +@Dao +interface StudentDao { + + @Insert(onConflict = ABORT) + fun insertAll(student: List): List + + @Delete + fun delete(student: Student) + + @Query("SELECT * FROM Students WHERE is_current = 1") + fun loadCurrent(): Maybe + + @Query("SELECT * FROM Students") + fun loadAll(): Maybe> + + @Query("UPDATE Students SET is_current = 1 WHERE id = :id") + fun updateCurrent(id: Long) + + @Query("UPDATE Students SET is_current = 0") + fun resetCurrent() +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt new file mode 100644 index 00000000..725a371a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt @@ -0,0 +1,21 @@ +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 new file mode 100644 index 00000000..abe21361 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt @@ -0,0 +1,24 @@ +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 deleted file mode 100644 index 78dff361..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java +++ /dev/null @@ -1,153 +0,0 @@ -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 org.greenrobot.greendao.annotation.Unique; - -import java.util.List; - -@Entity( - nameInDb = "Accounts", - active = true -) -public class Account { - - @Id(autoincrement = true) - private Long id; - - @Unique - @Property(nameInDb = "email") - private String email; - - @Property(nameInDb = "password") - private String password; - - @ToMany(referencedJoinProperty = "userId") - private List symbolList; - - /** - * 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 = 1104194311) - public Account(Long id, String email, String password) { - this.id = id; - this.email = email; - this.password = password; - } - - @Generated(hash = 882125521) - public Account() { - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getEmail() { - return this.email; - } - - public Account setEmail(String email) { - this.email = email; - return this; - } - - public String getPassword() { - return this.password; - } - - public Account setPassword(String password) { - this.password = password; - 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 = 822972496) - public List getSymbolList() { - if (symbolList == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - SymbolDao targetDao = daoSession.getSymbolDao(); - List symbolListNew = targetDao._queryAccount_SymbolList(id); - synchronized (this) { - if (symbolList == null) { - symbolList = symbolListNew; - } - } - } - return symbolList; - } - - /** - * Resets a to-many relationship, making the next get call to query for a fresh result. - */ - @Generated(hash = 1716801695) - public synchronized void resetSymbolList() { - symbolList = 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 = 1812283172) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getAccountDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java deleted file mode 100644 index 81235e59..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java +++ /dev/null @@ -1,254 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.DaoException; -import org.greenrobot.greendao.annotation.Entity; -import org.greenrobot.greendao.annotation.Generated; -import org.greenrobot.greendao.annotation.Id; -import org.greenrobot.greendao.annotation.Index; -import org.greenrobot.greendao.annotation.Property; -import org.greenrobot.greendao.annotation.Transient; - -import java.io.Serializable; - -@Entity( - nameInDb = "AttendanceLessons", - active = true, - indexes = {@Index(value = "dayId,date,number", unique = true)} -) -public class AttendanceLesson implements Serializable { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "day_id") - private Long dayId; - - @Property(nameInDb = "date") - private String date = ""; - - @Property(nameInDb = "number_of_lesson") - private int number = 0; - - @Property(nameInDb = "subject") - private String subject = ""; - - @Property(nameInDb = "presence") - private boolean presence = false; - - @Property(nameInDb = "absence_unexcused") - private boolean absenceUnexcused = false; - - @Property(nameInDb = "absence_excused") - private boolean absenceExcused = false; - - @Property(nameInDb = "unexcused_lateness") - private boolean unexcusedLateness = false; - - @Property(nameInDb = "absence_for_school_reasons") - private boolean absenceForSchoolReasons = false; - - @Property(nameInDb = "excused_lateness") - private boolean excusedLateness = false; - - @Property(nameInDb = "exemption") - private boolean exemption = false; - - @Transient - private String description = ""; - - private static final long serialVersionUID = 42L; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 1936953859) - private transient AttendanceLessonDao myDao; - - @Generated(hash = 1741231228) - public AttendanceLesson(Long id, Long dayId, String date, int number, - String subject, boolean presence, boolean absenceUnexcused, - boolean absenceExcused, boolean unexcusedLateness, - boolean absenceForSchoolReasons, boolean excusedLateness, - boolean exemption) { - this.id = id; - this.dayId = dayId; - this.date = date; - this.number = number; - this.subject = subject; - this.presence = presence; - this.absenceUnexcused = absenceUnexcused; - this.absenceExcused = absenceExcused; - this.unexcusedLateness = unexcusedLateness; - this.absenceForSchoolReasons = absenceForSchoolReasons; - this.excusedLateness = excusedLateness; - this.exemption = exemption; - } - - @Generated(hash = 921806575) - public AttendanceLesson() { - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public Long getDayId() { - return this.dayId; - } - - public void setDayId(Long dayId) { - this.dayId = dayId; - } - - public String getDate() { - return this.date; - } - - public AttendanceLesson setDate(String date) { - this.date = date; - return this; - } - - public int getNumber() { - return this.number; - } - - public AttendanceLesson setNumber(int number) { - this.number = number; - return this; - } - - public String getSubject() { - return this.subject; - } - - public AttendanceLesson setSubject(String subject) { - this.subject = subject; - return this; - } - - public boolean getPresence() { - return this.presence; - } - - public AttendanceLesson setPresence(boolean presence) { - this.presence = presence; - return this; - } - - public boolean getAbsenceUnexcused() { - return this.absenceUnexcused; - } - - public AttendanceLesson setAbsenceUnexcused(boolean absenceUnexcused) { - this.absenceUnexcused = absenceUnexcused; - return this; - } - - public boolean getAbsenceExcused() { - return this.absenceExcused; - } - - public AttendanceLesson setAbsenceExcused(boolean absenceExcused) { - this.absenceExcused = absenceExcused; - return this; - } - - public boolean getUnexcusedLateness() { - return this.unexcusedLateness; - } - - public AttendanceLesson setUnexcusedLateness(boolean unexcusedLateness) { - this.unexcusedLateness = unexcusedLateness; - return this; - } - - public boolean getAbsenceForSchoolReasons() { - return this.absenceForSchoolReasons; - } - - public AttendanceLesson setAbsenceForSchoolReasons(boolean absenceForSchoolReasons) { - this.absenceForSchoolReasons = absenceForSchoolReasons; - return this; - } - - public boolean getExcusedLateness() { - return this.excusedLateness; - } - - public AttendanceLesson setExcusedLateness(boolean excusedLateness) { - this.excusedLateness = excusedLateness; - return this; - } - - public boolean getExemption() { - return this.exemption; - } - - public AttendanceLesson setExemption(boolean exemption) { - this.exemption = exemption; - return this; - } - - public String getDescription() { - return description; - } - - public AttendanceLesson setDescription(String description) { - this.description = description; - return this; - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 128553479) - public void delete() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.delete(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 1942392019) - public void refresh() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.refresh(this); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}. - * Entity must attached to an entity context. - */ - @Generated(hash = 713229351) - public void update() { - if (myDao == null) { - throw new DaoException("Entity is detached from DAO context"); - } - myDao.update(this); - } - - /** called by internal mechanisms, do not call yourself. */ - @Generated(hash = 1157101112) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getAttendanceLessonDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java deleted file mode 100644 index c5472e98..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java +++ /dev/null @@ -1,263 +0,0 @@ -package io.github.wulkanowy.data.db.dao.entities; - -import org.greenrobot.greendao.DaoException; -import org.greenrobot.greendao.annotation.Entity; -import org.greenrobot.greendao.annotation.Generated; -import org.greenrobot.greendao.annotation.Id; -import org.greenrobot.greendao.annotation.Index; -import org.greenrobot.greendao.annotation.OrderBy; -import org.greenrobot.greendao.annotation.Property; -import org.greenrobot.greendao.annotation.ToMany; - -import java.util.List; - -@Entity( - nameInDb = "Days", - active = true, - indexes = {@Index(value = "weekId,date", unique = true)} -) -public class Day { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "week_id") - private Long weekId; - - @Property(nameInDb = "date") - private String date = ""; - - @Property(nameInDb = "day_name") - private String dayName = ""; - - @Property(nameInDb = "free_day") - private boolean freeDay = false; - - @Property(nameInDb = "free_day_name") - private String freeDayName = ""; - - @OrderBy("number ASC") - @ToMany(referencedJoinProperty = "dayId") - private List timetableLessons; - - @OrderBy("number ASC") - @ToMany(referencedJoinProperty = "dayId") - private List attendanceLessons; - - @ToMany(referencedJoinProperty = "dayId") - private List exams; - - /** - * 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 = 523139020) - public Day(Long id, Long weekId, String date, String dayName, boolean freeDay, - String freeDayName) { - this.id = id; - this.weekId = weekId; - this.date = date; - this.dayName = dayName; - this.freeDay = freeDay; - this.freeDayName = freeDayName; - } - - @Generated(hash = 866989762) - public Day() { - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public Long getWeekId() { - return this.weekId; - } - - public void setWeekId(Long weekId) { - this.weekId = weekId; - } - - public String getDate() { - return this.date; - } - - public Day setDate(String date) { - this.date = date; - return this; - } - - public String getDayName() { - return this.dayName; - } - - public Day setDayName(String dayName) { - this.dayName = dayName; - return this; - } - - public boolean getFreeDay() { - return this.freeDay; - } - - public Day setFreeDay(boolean freeDay) { - this.freeDay = freeDay; - return this; - } - - public String getFreeDayName() { - return this.freeDayName; - } - - public Day setFreeDayName(String freeDayName) { - this.freeDayName = freeDayName; - return this; - } - - /** - * To-many relationship, resolved on first access (and after reset). - * Changes to to-many relations are not persisted, make changes to the target entity. - */ - @Generated(hash = 218588195) - public List getTimetableLessons() { - if (timetableLessons == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - TimetableLessonDao targetDao = daoSession.getTimetableLessonDao(); - List timetableLessonsNew = targetDao - ._queryDay_TimetableLessons(id); - synchronized (this) { - if (timetableLessons == null) { - timetableLessons = timetableLessonsNew; - } - } - } - return timetableLessons; - } - - /** - * Resets a to-many relationship, making the next get call to query for a fresh result. - */ - @Generated(hash = 1687683740) - public synchronized void resetTimetableLessons() { - timetableLessons = null; - } - - /** - * To-many relationship, resolved on first access (and after reset). - * Changes to to-many relations are not persisted, make changes to the target entity. - */ - @Generated(hash = 1166820581) - public List getAttendanceLessons() { - if (attendanceLessons == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - AttendanceLessonDao targetDao = daoSession.getAttendanceLessonDao(); - List attendanceLessonsNew = targetDao - ._queryDay_AttendanceLessons(id); - synchronized (this) { - if (attendanceLessons == null) { - attendanceLessons = attendanceLessonsNew; - } - } - } - return attendanceLessons; - } - - /** - * Resets a to-many relationship, making the next get call to query for a fresh result. - */ - @Generated(hash = 1343075564) - public synchronized void resetAttendanceLessons() { - attendanceLessons = null; - } - - /** - * To-many relationship, resolved on first access (and after reset). - * Changes to to-many relations are not persisted, make changes to the target entity. - */ - @Generated(hash = 1231531946) - public List getExams() { - if (exams == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - ExamDao targetDao = daoSession.getExamDao(); - List examsNew = targetDao._queryDay_Exams(id); - synchronized (this) { - if (exams == null) { - exams = examsNew; - } - } - } - return exams; - } - - /** - * Resets a to-many relationship, making the next get call to query for a fresh result. - */ - @Generated(hash = 841969952) - public synchronized void resetExams() { - exams = null; - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. - * Entity must attached to an entity context. - */ - @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/Diary.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Diary.java deleted file mode 100644 index b0b145da..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Diary.java +++ /dev/null @@ -1,177 +0,0 @@ -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 = "Diaries", - active = true -) -public class Diary { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "student_id") - private Long studentId; - - @Property(nameInDb = "current") - private boolean current; - - @Property(nameInDb = "name") - private String name; - - @Property(nameInDb = "value") - private String value; - - @ToMany(referencedJoinProperty = "diaryId") - private List semesterList; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 21166549) - private transient DiaryDao myDao; - - @Generated(hash = 277096196) - public Diary(Long id, Long studentId, boolean current, String name, String value) { - this.id = id; - this.studentId = studentId; - this.current = current; - this.name = name; - this.value = value; - } - - @Generated(hash = 112123061) - public Diary() { - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public Long getStudentId() { - return this.studentId; - } - - public Diary setStudentId(Long studentId) { - this.studentId = studentId; - return this; - } - - public String getName() { - return this.name; - } - - public Diary setName(String name) { - this.name = name; - return this; - } - - public String getValue() { - return this.value; - } - - public Diary setValue(String value) { - this.value = value; - return this; - } - - public boolean getCurrent() { - return this.current; - } - - public Diary setCurrent(boolean current) { - this.current = current; - 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 = 1738383053) - public List getSemesterList() { - if (semesterList == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - SemesterDao targetDao = daoSession.getSemesterDao(); - List semesterListNew = targetDao._queryDiary_SemesterList(id); - synchronized (this) { - if (semesterList == null) { - semesterList = semesterListNew; - } - } - } - return semesterList; - } - - /** - * Resets a to-many relationship, making the next get call to query for a fresh result. - */ - @Generated(hash = 995060657) - public synchronized void resetSemesterList() { - semesterList = 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 = 629297785) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getDiaryDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Exam.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Exam.java deleted file mode 100644 index 543b6859..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Exam.java +++ /dev/null @@ -1,177 +0,0 @@ -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 = "Exams", - active = true, - indexes = {@Index(value = "dayId,entryDate,subjectAndGroup,type,teacher", unique = true)} -) - -public class Exam implements Serializable { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "day_id") - private Long dayId; - - @Property(nameInDb = "subject_and_group") - private String subjectAndGroup = ""; - - @Property(nameInDb = "type") - private String type = ""; - - @Property(nameInDb = "description") - private String description = ""; - - @Property(nameInDb = "teacher") - private String teacher = ""; - - @Property(nameInDb = "entry_date") - private String entryDate = ""; - - private static final long serialVersionUID = 42L; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 973692038) - private transient ExamDao myDao; - - @Generated(hash = 998653360) - public Exam(Long id, Long dayId, String subjectAndGroup, String type, String description, - String teacher, String entryDate) { - this.id = id; - this.dayId = dayId; - this.subjectAndGroup = subjectAndGroup; - this.type = type; - this.description = description; - this.teacher = teacher; - this.entryDate = entryDate; - } - - @Generated(hash = 945526930) - public Exam() { - } - - public Long getId() { - return id; - } - - public Exam setId(Long id) { - this.id = id; - return this; - } - - public Long getDayId() { - return this.dayId; - } - - public Exam setDayId(Long dayId) { - this.dayId = dayId; - return this; - } - - 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; - } - - /** - * 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 = 1730563422) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getExamDao() : null; - } - -} \ No newline at end of file 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 deleted file mode 100644 index 537b9e75..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Grade.java +++ /dev/null @@ -1,293 +0,0 @@ -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; - -@Entity( - nameInDb = "Grades", - active = true -) -public class Grade implements Serializable { - - @Id(autoincrement = true) - protected Long id; - - @Property(nameInDb = "semester_id") - private Long semesterId; - - @Property(nameInDb = "subject_id") - private Long subjectId; - - @Property(nameInDb = "subject") - private String subject = ""; - - @Property(nameInDb = "value") - protected String value = ""; - - @Property(nameInDb = "weight") - private String weight = ""; - - @Property(nameInDb = "date") - private String date = ""; - - @Property(nameInDb = "symbol") - private String symbol = ""; - - @Property(nameInDb = "color") - private String color = ""; - - @Property(nameInDb = "description") - private String description = ""; - - @Property(nameInDb = "teacher") - private String teacher = ""; - - @Property(nameInDb = "is_new") - private boolean isNew = false; - - @Property(nameInDb = "read") - private boolean read = true; - - private static final long serialVersionUID = 42L; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 681281562) - private transient GradeDao myDao; - - @Generated(hash = 2042976393) - public Grade() { - } - - @Generated(hash = 619853992) - public Grade(Long id, Long semesterId, Long subjectId, String subject, String value, - String weight, String date, String symbol, String color, String description, - String teacher, boolean isNew, boolean read) { - this.id = id; - this.semesterId = semesterId; - this.subjectId = subjectId; - this.subject = subject; - this.value = value; - this.weight = weight; - this.date = date; - this.symbol = symbol; - this.color = color; - this.description = description; - this.teacher = teacher; - this.isNew = isNew; - this.read = read; - } - - @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(semesterId, grade.semesterId) - .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) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(semesterId) - .append(subject) - .append(value) - .append(color) - .append(symbol) - .append(description) - .append(weight) - .append(date) - .append(teacher) - .toHashCode(); - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getValue() { - return this.value; - } - - public Grade setValue(String value) { - this.value = value; - return this; - } - - public Long getSemesterId() { - return this.semesterId; - } - - public Grade setSemesterId(Long semesterId) { - this.semesterId = semesterId; - return this; - } - - public String getSubject() { - return this.subject; - } - - public Grade setSubject(String subject) { - this.subject = subject; - return this; - } - - public String getColor() { - return this.color; - } - - public Grade setColor(String color) { - this.color = color; - return this; - } - - public String getSymbol() { - return this.symbol; - } - - public Grade setSymbol(String symbol) { - this.symbol = symbol; - return this; - } - - public String getDescription() { - return this.description; - } - - public Grade setDescription(String description) { - this.description = description; - return this; - } - - public String getWeight() { - return this.weight; - } - - public Grade setWeight(String weight) { - this.weight = weight; - return this; - } - - public String getDate() { - return this.date; - } - - public Grade setDate(String date) { - this.date = date; - return this; - } - - public String getTeacher() { - return this.teacher; - } - - public Grade setTeacher(String teacher) { - this.teacher = teacher; - return this; - } - - public boolean getIsNew() { - return this.isNew; - } - - public void setIsNew(boolean isNew) { - this.isNew = isNew; - } - - public boolean getRead() { - return this.read; - } - - public Grade setRead(boolean read) { - this.read = read; - return this; - } - - - public Long getSubjectId() { - return this.subjectId; - } - - - public void setSubjectId(Long subjectId) { - this.subjectId = subjectId; - } - - - /** - * 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/School.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/School.java deleted file mode 100644 index 59860e9e..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/School.java +++ /dev/null @@ -1,179 +0,0 @@ -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 = "Schools", - active = true -) -public class School { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "symbol_id") - private Long symbolId; - - @Property(nameInDb = "current") - private boolean current; - - @Property(nameInDb = "real_id") - private String realId; - - @Property(nameInDb = "name") - private String name; - - @ToMany(referencedJoinProperty = "schoolId") - private List studentList; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 1796006707) - private transient SchoolDao myDao; - - @Generated(hash = 975562398) - public School(Long id, Long symbolId, boolean current, String realId, - String name) { - this.id = id; - this.symbolId = symbolId; - this.current = current; - this.realId = realId; - this.name = name; - } - - @Generated(hash = 1579966795) - public School() { - } - - public Long getId() { - return this.id; - } - - public School setId(Long id) { - this.id = id; - return this; - } - - public Long getSymbolId() { - return this.symbolId; - } - - public School setSymbolId(Long symbolId) { - this.symbolId = symbolId; - return this; - } - - public boolean getCurrent() { - return this.current; - } - - public School setCurrent(boolean current) { - this.current = current; - return this; - } - - public String getRealId() { - return this.realId; - } - - public School setRealId(String realId) { - this.realId = realId; - return this; - } - - public String getName() { - return this.name; - } - - public School setName(String name) { - this.name = name; - 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 = 180118651) - public List getStudentList() { - if (studentList == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - StudentDao targetDao = daoSession.getStudentDao(); - List studentListNew = targetDao._querySchool_StudentList(id); - synchronized (this) { - if (studentList == null) { - studentList = studentListNew; - } - } - } - return studentList; - } - - /** - * Resets a to-many relationship, making the next get call to query for a fresh result. - */ - @Generated(hash = 1628625923) - public synchronized void resetStudentList() { - studentList = 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 = 234091322) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getSchoolDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Semester.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Semester.java deleted file mode 100644 index c739688f..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Semester.java +++ /dev/null @@ -1,208 +0,0 @@ -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 = "Semesters", - active = true -) -public class Semester { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "diary_id") - private Long diaryId; - - @Property(nameInDb = "current") - private boolean current; - - @Property(nameInDb = "name") - private String name; - - @Property(nameInDb = "value") - private String value; - - @ToMany(referencedJoinProperty = "semesterId") - private List subjectList; - - @ToMany(referencedJoinProperty = "semesterId") - private List gradeList; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 282930393) - private transient SemesterDao myDao; - - @Generated(hash = 1661077309) - public Semester(Long id, Long diaryId, boolean current, String name, String value) { - this.id = id; - this.diaryId = diaryId; - this.current = current; - this.name = name; - this.value = value; - } - - @Generated(hash = 58335877) - public Semester() { - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public Long getDiaryId() { - return this.diaryId; - } - - public Semester setDiaryId(Long diaryId) { - this.diaryId = diaryId; - return this; - } - - public String getName() { - return this.name; - } - - public Semester setName(String name) { - this.name = name; - return this; - } - - public String getValue() { - return this.value; - } - - public Semester setValue(String value) { - this.value = value; - return this; - } - - public boolean getCurrent() { - return this.current; - } - - public Semester setCurrent(boolean current) { - this.current = current; - 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 = 723353662) - 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._querySemester_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; - } - - /** - * 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 = 390330007) - 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._querySemester_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; - } - - /** called by internal mechanisms, do not call yourself. */ - @Generated(hash = 676204164) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getSemesterDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Student.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Student.java deleted file mode 100644 index 1d545473..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Student.java +++ /dev/null @@ -1,178 +0,0 @@ -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 = "Students", - active = true -) -public class Student { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "school_id") - private Long schoolId; - - @Property(nameInDb = "current") - private boolean current; - - @Property(nameInDb = "real_id") - private String realId; - - @Property(nameInDb = "name") - private String name; - - @ToMany(referencedJoinProperty = "studentId") - private List diaryList; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 1943931642) - private transient StudentDao myDao; - - @Generated(hash = 470181623) - public Student(Long id, Long schoolId, boolean current, String realId, String name) { - this.id = id; - this.schoolId = schoolId; - this.current = current; - this.realId = realId; - this.name = name; - } - - @Generated(hash = 1556870573) - public Student() { - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public Long getSchoolId() { - return this.schoolId; - } - - public Student setSchoolId(Long schoolId) { - this.schoolId = schoolId; - return this; - } - - public String getRealId() { - return this.realId; - } - - public Student setRealId(String realId) { - this.realId = realId; - return this; - } - - public String getName() { - return this.name; - } - - public Student setName(String name) { - this.name = name; - return this; - } - - public boolean getCurrent() { - return this.current; - } - - public Student setCurrent(boolean current) { - this.current = current; - 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 = 508305571) - public List getDiaryList() { - if (diaryList == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - DiaryDao targetDao = daoSession.getDiaryDao(); - List diaryListNew = targetDao._queryStudent_DiaryList(id); - synchronized (this) { - if (diaryList == null) { - diaryList = diaryListNew; - } - } - } - return diaryList; - } - - /** - * Resets a to-many relationship, making the next get call to query for a fresh result. - */ - @Generated(hash = 1078514341) - public synchronized void resetDiaryList() { - diaryList = 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 = 1701634981) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getStudentDao() : 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 deleted file mode 100644 index 285cd7c0..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Subject.java +++ /dev/null @@ -1,176 +0,0 @@ -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 = "semester_id") - private Long semesterId; - - @Property(nameInDb = "name") - private String name; - - @Property(nameInDb = "predicted_rating") - private String predictedRating; - - @Property(nameInDb = "final_rating") - private String finalRating; - - @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 = 1817932538) - public Subject(Long id, Long semesterId, String name, String predictedRating, - String finalRating) { - this.id = id; - this.semesterId = semesterId; - this.name = name; - this.predictedRating = predictedRating; - this.finalRating = finalRating; - } - - @Generated(hash = 1617906264) - public Subject() { - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public Long getSemesterId() { - return this.semesterId; - } - - public Subject setSemesterId(Long semesterId) { - this.semesterId = semesterId; - return this; - } - - public String getName() { - return this.name; - } - - public Subject setName(String name) { - this.name = name; - return this; - } - - public String getPredictedRating() { - return this.predictedRating; - } - - public Subject setPredictedRating(String predictedRating) { - this.predictedRating = predictedRating; - return this; - } - - public String getFinalRating() { - return this.finalRating; - } - - public Subject setFinalRating(String finalRating) { - this.finalRating = finalRating; - 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); - } - - /** - * 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; - } - - /** 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/Symbol.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Symbol.java deleted file mode 100644 index 7cc46111..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Symbol.java +++ /dev/null @@ -1,175 +0,0 @@ -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 = "Symbols", - active = true -) -public class Symbol { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "user_id") - private Long userId; - - @Property(nameInDb = "host") - private String host; - - @Property(nameInDb = "symbol") - private String symbol; - - @Property(nameInDb = "type") - private String type; - - @ToMany(referencedJoinProperty = "symbolId") - private List schoolList; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 684907977) - private transient SymbolDao myDao; - - @Generated(hash = 1034469460) - public Symbol(Long id, Long userId, String host, String symbol, String type) { - this.id = id; - this.userId = userId; - this.host = host; - this.symbol = symbol; - this.type = type; - } - - @Generated(hash = 460475327) - public Symbol() { - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public Long getUserId() { - return this.userId; - } - - public Symbol setUserId(Long userId) { - this.userId = userId; - return this; - } - - public String getHost() { - return this.host; - } - - public Symbol setHost(String host) { - this.host = host; - return this; - } - - public String getSymbol() { - return this.symbol; - } - - public Symbol setSymbol(String symbol) { - this.symbol = symbol; - return this; - } - - public String getType() { - return this.type; - } - - public Symbol setType(String type) { - this.type = type; - 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 = 1733082867) - public List getSchoolList() { - if (schoolList == null) { - final DaoSession daoSession = this.daoSession; - if (daoSession == null) { - throw new DaoException("Entity is detached from DAO context"); - } - SchoolDao targetDao = daoSession.getSchoolDao(); - List schoolListNew = targetDao._querySymbol_SchoolList(id); - synchronized (this) { - if (schoolList == null) { - schoolList = schoolListNew; - } - } - } - return schoolList; - } - - /** Resets a to-many relationship, making the next get call to query for a fresh result. */ - @Generated(hash = 1757777300) - public synchronized void resetSchoolList() { - schoolList = 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 = 632145708) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getSymbolDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java deleted file mode 100644 index f3fdf6a8..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java +++ /dev/null @@ -1,332 +0,0 @@ -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.Index; -import org.greenrobot.greendao.annotation.Property; - -import java.io.Serializable; - -@Entity( - nameInDb = "TimetableLessons", - active = true, - indexes = {@Index(value = "dayId,date,number,startTime,endTime", unique = true)} -) -public class TimetableLesson implements Serializable { - - private static final long serialVersionUID = 42L; - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "day_id") - private Long dayId; - - @Property(nameInDb = "number") - private int number = 0; - - @Property(nameInDb = "subject") - private String subject = ""; - - @Property(nameInDb = "teacher") - private String teacher = ""; - - @Property(nameInDb = "room") - private String room = ""; - - @Property(nameInDb = "description") - private String description = ""; - - @Property(nameInDb = "group") - private String group = ""; - - @Property(nameInDb = "start_time") - private String startTime = ""; - - @Property(nameInDb = "end_time") - private String endTime = ""; - - @Property(nameInDb = "date") - private String date = ""; - - @Property(nameInDb = "empty") - private boolean empty = false; - - @Property(nameInDb = "division_into_groups") - private boolean divisionIntoGroups = false; - - @Property(nameInDb = "planning") - private boolean planning = false; - - @Property(nameInDb = "realized") - private boolean realized = false; - - @Property(nameInDb = "moved_canceled") - private boolean movedOrCanceled = false; - - @Property(nameInDb = "new_moved_in_canceled") - private boolean newMovedInOrChanged = false; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** Used for active entity operations. */ - @Generated(hash = 1119360138) - private transient TimetableLessonDao myDao; - - @Generated(hash = 1665905034) - public TimetableLesson(Long id, Long dayId, int number, String subject, String teacher, - String room, String description, String group, String startTime, String endTime, - String date, boolean empty, boolean divisionIntoGroups, boolean planning, - boolean realized, boolean movedOrCanceled, boolean newMovedInOrChanged) { - this.id = id; - this.dayId = dayId; - this.number = number; - this.subject = subject; - this.teacher = teacher; - this.room = room; - this.description = description; - this.group = group; - this.startTime = startTime; - this.endTime = endTime; - this.date = date; - this.empty = empty; - this.divisionIntoGroups = divisionIntoGroups; - this.planning = planning; - this.realized = realized; - this.movedOrCanceled = movedOrCanceled; - this.newMovedInOrChanged = newMovedInOrChanged; - } - - @Generated(hash = 1878030142) - public TimetableLesson() { - } - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public Long getDayId() { - return this.dayId; - } - - public TimetableLesson setDayId(Long dayId) { - this.dayId = dayId; - return this; - } - - public int getNumber() { - return this.number; - } - - public TimetableLesson setNumber(int number) { - this.number = number; - return this; - } - - public String getSubject() { - return this.subject; - } - - public TimetableLesson setSubject(String subject) { - this.subject = subject; - return this; - } - - public String getTeacher() { - return this.teacher; - } - - public TimetableLesson setTeacher(String teacher) { - this.teacher = teacher; - return this; - } - - public String getRoom() { - return this.room; - } - - public TimetableLesson setRoom(String room) { - this.room = room; - return this; - } - - public String getDescription() { - return this.description; - } - - public TimetableLesson setDescription(String description) { - this.description = description; - return this; - } - - public String getGroup() { - return this.group; - } - - public TimetableLesson setGroup(String group) { - this.group = group; - return this; - } - - public String getStartTime() { - return this.startTime; - } - - public TimetableLesson setStartTime(String startTime) { - this.startTime = startTime; - return this; - } - - public String getEndTime() { - return this.endTime; - } - - public TimetableLesson setEndTime(String endTime) { - this.endTime = endTime; - return this; - } - - public String getDate() { - return this.date; - } - - public TimetableLesson setDate(String date) { - this.date = date; - return this; - } - - public boolean getEmpty() { - return this.empty; - } - - public TimetableLesson setEmpty(boolean empty) { - this.empty = empty; - return this; - } - - public boolean getDivisionIntoGroups() { - return this.divisionIntoGroups; - } - - public TimetableLesson setDivisionIntoGroups(boolean divisionIntoGroups) { - this.divisionIntoGroups = divisionIntoGroups; - return this; - } - - public boolean getPlanning() { - return this.planning; - } - - public TimetableLesson setPlanning(boolean planning) { - this.planning = planning; - return this; - } - - public boolean getRealized() { - return this.realized; - } - - public TimetableLesson setRealized(boolean realized) { - this.realized = realized; - return this; - } - - public boolean getMovedOrCanceled() { - return this.movedOrCanceled; - } - - public TimetableLesson setMovedOrCanceled(boolean movedOrCanceled) { - this.movedOrCanceled = movedOrCanceled; - return this; - } - - public boolean getNewMovedInOrChanged() { - return this.newMovedInOrChanged; - } - - public TimetableLesson setNewMovedInOrChanged(boolean newMovedInOrChanged) { - this.newMovedInOrChanged = newMovedInOrChanged; - return this; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - TimetableLesson lesson = (TimetableLesson) o; - - return new EqualsBuilder() - .append(number, lesson.number) - .append(startTime, lesson.startTime) - .append(endTime, lesson.endTime) - .append(date, lesson.date) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(number) - .append(startTime) - .append(endTime) - .append(date) - .toHashCode(); - } - - /** - * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}. - * Entity must attached to an entity context. - */ - @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 = 1885258429) - public void __setDaoSession(DaoSession daoSession) { - this.daoSession = daoSession; - myDao = daoSession != null ? daoSession.getTimetableLessonDao() : null; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java deleted file mode 100644 index 1edfa245..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java +++ /dev/null @@ -1,194 +0,0 @@ -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 = "diaryId,startDayDate", unique = true)} -) -public class Week { - - @Id(autoincrement = true) - private Long id; - - @Property(nameInDb = "diary_id") - private Long diaryId; - - @Property(nameInDb = "start_day_date") - private String startDayDate = ""; - - @Property(nameInDb = "attendance_synced") - private boolean attendanceSynced = false; - - @Property(nameInDb = "timetable_synced") - private boolean timetableSynced = false; - - @Property(nameInDb = "exams_synced") - private boolean examsSynced = false; - - @ToMany(referencedJoinProperty = "weekId") - private List dayList; - - /** - * Used to resolve relations - */ - @Generated(hash = 2040040024) - private transient DaoSession daoSession; - - /** - * Used for active entity operations. - */ - @Generated(hash = 1019310398) - private transient WeekDao myDao; - - @Generated(hash = 23357599) - public Week(Long id, Long diaryId, String startDayDate, boolean attendanceSynced, - boolean timetableSynced, boolean examsSynced) { - this.id = id; - this.diaryId = diaryId; - this.startDayDate = startDayDate; - this.attendanceSynced = attendanceSynced; - this.timetableSynced = timetableSynced; - this.examsSynced = examsSynced; - } - - @Generated(hash = 2135529658) - public Week() { - } - - public Long getId() { - return id; - } - - public Week setId(Long id) { - this.id = id; - return this; - } - - public Long getDiaryId() { - return diaryId; - } - - public Week setDiaryId(Long diaryId) { - this.diaryId = diaryId; - return this; - } - - public String getStartDayDate() { - return startDayDate; - } - - public Week setStartDayDate(String startDayDate) { - this.startDayDate = startDayDate; - return this; - } - - public boolean getAttendanceSynced() { - return this.attendanceSynced; - } - - public Week setAttendanceSynced(boolean attendanceSynced) { - this.attendanceSynced = attendanceSynced; - return this; - } - - public boolean getTimetableSynced() { - return this.timetableSynced; - } - - public Week setTimetableSynced(boolean timetableSynced) { - this.timetableSynced = timetableSynced; - return this; - } - - public Week setExamsSynced(boolean examsSynced) { - this.examsSynced = examsSynced; - return this; - } - - public boolean getExamsSynced() { - return examsSynced; - } - - /** - * 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/dao/migrations/Migration23.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration23.java deleted file mode 100644 index b2a41ad1..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration23.java +++ /dev/null @@ -1,114 +0,0 @@ -package io.github.wulkanowy.data.db.dao.migrations; - -import android.database.Cursor; -import android.os.AsyncTask; - -import org.greenrobot.greendao.database.Database; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.api.generic.Diary; -import io.github.wulkanowy.data.db.dao.DbHelper; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.utils.security.Scrambler; - -public class Migration23 implements DbHelper.Migration { - - @Override - public Integer getVersion() { - return 23; - } - - @Override - public void runMigration(final Database db, final SharedPrefContract sharedPref, final Vulcan vulcan) throws Exception { - createDiaryTable(db); - migrateAccountsTable(db); - - final Map user = getAccountData(db); - vulcan.setCredentials( - user.get("email"), - Scrambler.decrypt(user.get("email"), user.get("password")), - user.get("symbol"), - user.get("school_id"), - "", // inserted in code bellow - "" - ); - - AsyncTask.execute(new Runnable() { - @Override - public void run() { - try { - insertDiaries(db, vulcan.getStudentAndParent().getDiaries()); - updateAccount(db, vulcan.getStudentAndParent().getStudentID()); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - - private void createDiaryTable(Database db) { - db.execSQL("DROP TABLE IF EXISTS Diaries"); - db.execSQL("CREATE TABLE IF NOT EXISTS \"Diaries\" (" + // - "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id - "\"STUDENT_ID\" TEXT," + // 1: studentId - "\"NAME\" TEXT," + // 2: name - "\"VALUE\" TEXT," + // 3: value - "\"IS_CURRENT\" INTEGER NOT NULL );"); // 4: isCurrent - } - - private void migrateAccountsTable(Database db) { - db.execSQL("DROP TABLE IF EXISTS tmp_account"); - db.execSQL("ALTER TABLE Accounts RENAME TO tmp_account"); - db.execSQL("CREATE TABLE IF NOT EXISTS \"Accounts\" (" + // - "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id - "\"REAL_ID\" TEXT," + // 1: realId - "\"SYMBOL\" TEXT," + // 2: symbol - "\"SCHOOL_ID\" TEXT," + // 3: schoolId - "\"NAME\" TEXT," + // 4: name - "\"E_MAIL\" TEXT," + // 5: email - "\"PASSWORD\" TEXT);"); // 6: password - // Add Indexes - db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS IDX_Accounts_REAL_ID ON \"Accounts\" (\"REAL_ID\" ASC);"); - db.execSQL("INSERT INTO Accounts(NAME, E_MAIL, PASSWORD, SYMBOL, SCHOOL_ID)" + - "SELECT `NAME`, `E-MAIL`, `PASSWORD`, `SYMBOL`, `SNPID` FROM tmp_account"); - db.execSQL("DROP TABLE tmp_account"); - } - - private Map getAccountData(Database db) { - Map values = new HashMap<>(); - Cursor cursor = db.rawQuery("SELECT SYMBOL, SCHOOL_ID, NAME, E_MAIL, PASSWORD FROM Accounts", null); - - if (cursor.moveToFirst()) { - do { - values.put("symbol", cursor.getString(cursor.getColumnIndex("SYMBOL"))); - values.put("school_id", cursor.getString(cursor.getColumnIndex("SCHOOL_ID"))); - values.put("name", cursor.getString(cursor.getColumnIndex("NAME"))); - values.put("email", cursor.getString(cursor.getColumnIndex("E_MAIL"))); - values.put("password", cursor.getString(cursor.getColumnIndex("PASSWORD"))); - } while (cursor.moveToNext()); - } - - cursor.close(); - - return values; - } - - private void insertDiaries(Database db, List list) { - for (Diary diary : list) { - db.execSQL("INSERT INTO Diaries(STUDENT_ID, NAME, VALUE, IS_CURRENT) VALUES(" + - "\"" + diary.getId() + "\"," + - "\"" + diary.getName() + "\"," + - "\"" + diary.getId() + "\"," + - "\"" + (diary.isCurrent() ? "1" : "0") + "\"" + - ")"); - } - } - - private void updateAccount(Database db, String realId) { - db.execSQL("UPDATE Accounts SET REAL_ID = ?", new String[]{realId}); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration26.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration26.java deleted file mode 100644 index 332f2dd2..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration26.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.data.db.dao.migrations; - -import org.greenrobot.greendao.database.Database; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.data.db.dao.DbHelper; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; - -public class Migration26 implements DbHelper.Migration { - - @Override - public Integer getVersion() { - return 26; - } - - @Override - public void runMigration(final Database db, final SharedPrefContract sharedPref, final Vulcan vulcan) throws Exception { - throw new Exception("No migrations"); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration27.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration27.java deleted file mode 100644 index 922079f4..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration27.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.dao.migrations; - -import org.greenrobot.greendao.database.Database; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.data.db.dao.DbHelper; -import io.github.wulkanowy.data.db.dao.entities.ExamDao; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; - -public class Migration27 implements DbHelper.Migration { - - @Override - public Integer getVersion() { - return 27; - } - - @Override - public void runMigration(Database db, SharedPrefContract sharedPref, Vulcan vulcan) { - ExamDao.dropTable(db, true); - ExamDao.createTable(db, true); - - db.execSQL("UPDATE Weeks SET exams_synced = 0"); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration28.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration28.java deleted file mode 100644 index 3970df4c..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration28.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.data.db.dao.migrations; - -import org.greenrobot.greendao.database.Database; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.data.db.dao.DbHelper; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; - -public class Migration28 implements DbHelper.Migration { - - @Override - public Integer getVersion() { - return 28; - } - - @Override - public void runMigration(final Database db, final SharedPrefContract sharedPref, final Vulcan vulcan) throws Exception { - throw new Exception("No migrations"); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration29.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration29.kt deleted file mode 100644 index e318b5fd..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration29.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.data.db.dao.migrations - -import android.database.Cursor - -import org.greenrobot.greendao.database.Database - -import io.github.wulkanowy.api.Vulcan -import io.github.wulkanowy.data.db.dao.DbHelper -import io.github.wulkanowy.data.db.shared.SharedPrefContract - -class Migration29 : DbHelper.Migration { - - override fun getVersion(): Int? { - return 29 - } - - override fun runMigration(db: Database, sharedPref: SharedPrefContract, vulcan: Vulcan) { - createSchoolsTable(db) - modifyStudents(db) - insertSchool(db, getRealSchoolId(db)) - } - - private fun createSchoolsTable(db: Database) { - db.execSQL("DROP TABLE IF EXISTS \"Schools\";") - db.execSQL("CREATE TABLE IF NOT EXISTS \"Schools\" (" + // - "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id - "\"symbol_id\" INTEGER," + // 1: symbolId - "\"current\" INTEGER NOT NULL ," + // 2: current - "\"real_id\" TEXT," + // 3: realId - "\"name\" TEXT);") // 4: name - } - - private fun modifyStudents(db: Database) { - db.execSQL("ALTER TABLE Students ADD COLUMN school_id INTEGER") - db.execSQL("UPDATE Students SET school_id = '1'") - } - - private fun getRealSchoolId(db: Database): String { - var cursor: Cursor? = null - try { - cursor = db.rawQuery("SELECT school_id FROM Symbols WHERE _id=?", arrayOf("1")) - - return if (cursor!!.count > 0) { - cursor.moveToFirst() - cursor.getString(cursor.getColumnIndex("school_id")) - } else "" - } finally { - cursor!!.close() - } - } - - private fun insertSchool(db: Database, realId: String) { - db.execSQL("INSERT INTO Schools(symbol_id, current, real_id, name) VALUES(" + - "\"1\"," + - "\"1\"," + - "\"" + realId + "\"," + - "\"Uczeń\"" + - ")") - } -} 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 new file mode 100644 index 00000000..3c58971a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt @@ -0,0 +1,41 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import org.threeten.bp.LocalDate +import java.io.Serializable + +@Entity(tableName = "Attendance") +data class Attendance( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + val date: LocalDate, + + val number: Int, + + val subject: String, + + val name: String, + + val presence: Boolean, + + val absence: Boolean, + + val exemption: Boolean, + + val lateness: Boolean, + + val excused: Boolean, + + val deleted: Boolean +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt new file mode 100644 index 00000000..d2e1f174 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt @@ -0,0 +1,43 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import org.threeten.bp.Month +import java.io.Serializable + +@Entity(tableName = "AttendanceSummary") +data class AttendanceSummary( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + @ColumnInfo(name = "subject_id") + val subjectId: Int, + + val month: Month, + + val presence: Int, + + val absence: Int, + + @ColumnInfo(name = "absence_excused") + val absenceExcused: Int, + + @ColumnInfo(name = "absence_for_school_reasons") + val absenceForSchoolReasons: Int, + + val lateness: Int, + + @ColumnInfo(name = "lateness_excused") + val latenessExcused: Int, + + val exemption: Int +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt new file mode 100644 index 00000000..775f3f55 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt @@ -0,0 +1,40 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import org.threeten.bp.LocalDate +import java.io.Serializable + +@Entity(tableName = "CompletedLesson") +data class CompletedLesson( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + val date: LocalDate, + + val number: Int, + + val subject: String, + + val topic: String, + + val teacher: String, + + @ColumnInfo(name = "teacher_symbol") + val teacherSymbol: String, + + val substitution: String, + + val absence: String, + + val resources: String +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt new file mode 100644 index 00000000..9ae795e7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import org.threeten.bp.LocalDate +import java.io.Serializable + +@Entity(tableName = "Exams") +data class Exam( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + val date: LocalDate, + + @ColumnInfo(name = "entry_date") + val entryDate: LocalDate, + + val subject: String, + + val group: String, + + val type: String, + + val description: String, + + val teacher: String, + + @ColumnInfo(name = "teacher_symbol") + val teacherSymbol: String +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt new file mode 100644 index 00000000..1221a7aa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import org.threeten.bp.LocalDate +import java.io.Serializable + +@Entity(tableName = "Grades") +data class Grade( + + @ColumnInfo(name = "semester_id") + val semesterId: Int, + + @ColumnInfo(name = "student_id") + val studentId: Int, + + val subject: String, + + val entry: String, + + val value: Int, + + val modifier: Double, + + val comment: String, + + val color: String, + + @ColumnInfo(name = "grade_symbol") + val gradeSymbol: String, + + val description: String, + + val weight: String, + + val weightValue: Double, + + val date: LocalDate, + + val teacher: String +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_read") + var isRead: Boolean = true + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt new file mode 100644 index 00000000..8ad8b8b8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt @@ -0,0 +1,27 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "GradesStatistics") +data class GradeStatistics( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "semester_id") + val semesterId: Int, + + val subject: String, + + val grade: Int, + + val amount: Int, + + @ColumnInfo(name = "is_semester") + val semester: Boolean +) { + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt new file mode 100644 index 00000000..6e29112b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "GradesSummary") +data class GradeSummary( + + @ColumnInfo(name = "semester_id") + val semesterId: Int, + + @ColumnInfo(name = "student_id") + val studentId: Int, + + val position: Int, + + val subject: String, + + @ColumnInfo(name = "predicted_grade") + val predictedGrade: String, + + @ColumnInfo(name = "final_grade") + val finalGrade: String, + + @ColumnInfo(name = "proposed_points") + val proposedPoints: String, + + @ColumnInfo(name = "final_points") + val finalPoints: String, + + @ColumnInfo(name = "points_sum") + val pointsSum: String, + + val average: Double +) { + @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 new file mode 100644 index 00000000..a22df096 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import org.threeten.bp.LocalDate +import java.io.Serializable + +@Entity(tableName = "Homework") +data class Homework( + + @ColumnInfo(name = "semester_id") + val semesterId: Int, + + @ColumnInfo(name = "student_id") + val studentId: Int, + + val date: LocalDate, + + @ColumnInfo(name = "entry_date") + val entryDate: LocalDate, + + val subject: String, + + val content: String, + + val teacher: String, + + @ColumnInfo(name = "teacher_symbol") + val teacherSymbol: String + +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt new file mode 100644 index 00000000..5b9130f5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt @@ -0,0 +1,27 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import org.threeten.bp.LocalDate +import java.io.Serializable + +@Entity(tableName = "LuckyNumbers") +data class LuckyNumber ( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + val date: LocalDate, + + @ColumnInfo(name = "lucky_number") + val luckyNumber: Int + +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt new file mode 100644 index 00000000..48b4fd02 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt @@ -0,0 +1,55 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import org.threeten.bp.LocalDateTime +import java.io.Serializable + +@Entity(tableName = "Messages") +data class Message( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "real_id") + val realId: Int, + + @ColumnInfo(name = "message_id") + val messageId: Int, + + @ColumnInfo(name = "sender_name") + val sender: String, + + @ColumnInfo(name = "sender_id") + val senderId: Int, + + @ColumnInfo(name = "recipient_name") + val recipient: String, + + val subject: String, + + val date: LocalDateTime, + + @ColumnInfo(name = "folder_id") + val folderId: Int, + + var unread: Boolean, + + @ColumnInfo(name = "unread_by") + val unreadBy: Int, + + @ColumnInfo(name = "read_by") + val readBy: Int, + + val removed: Boolean +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true + + var content: String? = null +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt new file mode 100644 index 00000000..f67ed599 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt @@ -0,0 +1,25 @@ +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 = "MobileDevices") +data class MobileDevice( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "device_id") + val deviceId: Int, + + val name: String, + + val date: LocalDateTime +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} 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 new file mode 100644 index 00000000..5f3a92ab --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import org.threeten.bp.LocalDate +import java.io.Serializable + +@Entity(tableName = "Notes") +data class Note( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + val date: LocalDate, + + val teacher: String, + + val category: String, + + val content: String +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + @ColumnInfo(name = "is_read") + var isRead: Boolean = true + + @ColumnInfo(name = "is_notified") + var isNotified: Boolean = true +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt new file mode 100644 index 00000000..3021da72 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity(tableName = "Recipients") +data class Recipient( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "real_id") + val realId: String, + + val name: String, + + @ColumnInfo(name = "real_name") + val realName: String, + + @ColumnInfo(name = "login_id") + val loginId: Int, + + @ColumnInfo(name = "unit_id") + val unitId: Int, + + val role: Int, + + val hash: String + +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + override fun toString() = name +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt new file mode 100644 index 00000000..601d8aac --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity(tableName = "ReportingUnits") +data class ReportingUnit( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "real_id") + val realId: Int, + + @ColumnInfo(name = "short") + val shortName: String, + + @ColumnInfo(name = "sender_id") + val senderId: Int, + + @ColumnInfo(name = "sender_name") + val senderName: String, + + val roles: List + +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt new file mode 100644 index 00000000..6c06be11 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt @@ -0,0 +1,46 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey +import org.threeten.bp.LocalDate + +@Entity(tableName = "Semesters", indices = [Index(value = ["student_id", "diary_id", "semester_id"], unique = true)]) +data class Semester( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + @ColumnInfo(name = "diary_name") + val diaryName: String, + + @ColumnInfo(name = "school_year") + val schoolYear: Int, + + @ColumnInfo(name = "semester_id") + val semesterId: Int, + + @ColumnInfo(name = "semester_name") + val semesterName: Int, + + @ColumnInfo(name = "is_current") + val isCurrent: Boolean, + + val start: LocalDate, + + val end: LocalDate, + + @ColumnInfo(name = "class_id") + val classId: Int, + + @ColumnInfo(name = "unit_id") + val unitId: Int +) { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt new file mode 100644 index 00000000..13c5ee08 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey +import org.threeten.bp.LocalDateTime +import java.io.Serializable + +@Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id", "class_id"], unique = true)]) +data class Student( + + val endpoint: String, + + val loginType: String, + + val email: String, + + var password: String, + + val symbol: String, + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "student_name") + val studentName: String, + + @ColumnInfo(name = "school_id") + val schoolSymbol: String, + + @ColumnInfo(name = "school_name") + val schoolName: String, + + @ColumnInfo(name = "class_name") + val className: String, + + @ColumnInfo(name = "class_id") + val classId: Int, + + @ColumnInfo(name = "is_current") + val isCurrent: Boolean, + + @ColumnInfo(name = "registration_date") + val registrationDate: LocalDateTime +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt new file mode 100644 index 00000000..dbaa6f4e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity(tableName = "Subjects") +data class Subject( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + @ColumnInfo(name = "real_id") + val realId: Int, + + val name: String +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt new file mode 100644 index 00000000..9bc3d214 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDateTime +import java.io.Serializable + +@Entity(tableName = "Timetable") +data class Timetable( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + val number: Int, + + val start: LocalDateTime, + + val end: LocalDateTime, + + val date: LocalDate, + + val subject: String, + + val subjectOld: String, + + val group: String, + + val room: String, + + val roomOld: String, + + val teacher: String, + + val teacherOld: String, + + val info: String, + + val changes: Boolean, + + val canceled: Boolean +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt new file mode 100644 index 00000000..c26a02d1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt @@ -0,0 +1,11 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration10 : Migration(9, 10) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Grades_Summary RENAME TO GradesSummary") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt new file mode 100644 index 00000000..6d129bca --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration11 : Migration(10, 11) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Grades_temp ( + id INTEGER PRIMARY KEY NOT NULL, + is_read INTEGER NOT NULL, + is_notified INTEGER NOT NULL, + semester_id INTEGER NOT NULL, + student_id INTEGER NOT NULL, + subject TEXT NOT NULL, + entry TEXT NOT NULL, + value INTEGER NOT NULL, + modifier REAL NOT NULL, + comment TEXT NOT NULL, + color TEXT NOT NULL, + grade_symbol TEXT NOT NULL, + description TEXT NOT NULL, + weight TEXT NOT NULL, + weightValue REAL NOT NULL, + date INTEGER NOT NULL, + teacher TEXT NOT NULL + ) + """) + database.execSQL("INSERT INTO Grades_temp SELECT * FROM Grades") + database.execSQL("DROP TABLE Grades") + database.execSQL("ALTER TABLE Grades_temp RENAME TO Grades") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt new file mode 100644 index 00000000..1dc38e14 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt @@ -0,0 +1,69 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration12 : Migration(11, 12) { + + override fun migrate(database: SupportSQLiteDatabase) { + createTempStudentsTable(database) + replaceStudentTable(database) + updateStudentsWithClassId(database, getStudentsIds(database)) + removeStudentsWithNoClassId(database) + ensureThereIsOnlyOneCurrentStudent(database) + } + + private fun createTempStudentsTable(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Students_tmp ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + endpoint TEXT NOT NULL, + loginType TEXT NOT NULL, + email TEXT NOT NULL, + password TEXT NOT NULL, + symbol TEXT NOT NULL, + student_id INTEGER NOT NULL, + student_name TEXT NOT NULL, + school_id TEXT NOT NULL, + school_name TEXT NOT NULL, + is_current INTEGER NOT NULL, + registration_date INTEGER NOT NULL, + class_id INTEGER NOT NULL + ) + """) + database.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students_tmp (email, symbol, student_id, school_id, class_id)") + } + + private fun replaceStudentTable(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") + database.execSQL("INSERT INTO Students_tmp SELECT * FROM Students") + database.execSQL("DROP TABLE Students") + database.execSQL("ALTER TABLE Students_tmp RENAME TO Students") + } + + private fun getStudentsIds(database: SupportSQLiteDatabase): List { + val students = mutableListOf() + val studentsCursor = database.query("SELECT student_id FROM Students") + if (studentsCursor.moveToFirst()) { + do { + students.add(studentsCursor.getInt(0)) + } while (studentsCursor.moveToNext()) + } + return students + } + + private fun updateStudentsWithClassId(database: SupportSQLiteDatabase, students: List) { + students.forEach { + database.execSQL("UPDATE Students SET class_id = IFNULL((SELECT class_id FROM Semesters WHERE student_id = $it), 0) WHERE student_id = $it") + } + } + + private fun removeStudentsWithNoClassId(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Students WHERE class_id = 0") + } + + private fun ensureThereIsOnlyOneCurrentStudent(database: SupportSQLiteDatabase) { + database.execSQL("UPDATE Students SET is_current = 0") + database.execSQL("UPDATE Students SET is_current = 1 WHERE id = (SELECT MAX(id) FROM Students)") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt new file mode 100644 index 00000000..0cf8cd9b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt @@ -0,0 +1,64 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration13 : Migration(12, 13) { + + override fun migrate(database: SupportSQLiteDatabase) { + addClassNameToStudents(database, getStudentsIds(database)) + updateSemestersTable(database) + markAtLeastAndOnlyOneSemesterAtCurrent(database, getStudentsAndClassIds(database)) + clearMessagesTable(database) + } + + private fun addClassNameToStudents(database: SupportSQLiteDatabase, students: List>) { + database.execSQL("ALTER TABLE Students ADD COLUMN class_name TEXT DEFAULT \"\" NOT NULL") + + students.forEach { (id, name) -> + val schoolName = name.substringAfter(" - ") + val className = name.substringBefore(" - ", "").replace("Klasa ", "") + database.execSQL("UPDATE Students SET class_name = '$className' WHERE id = '$id'") + database.execSQL("UPDATE Students SET school_name = '$schoolName' WHERE id = '$id'") + } + } + + private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList> { + val students = mutableListOf>() + val studentsCursor = database.query("SELECT id, school_name FROM Students") + if (studentsCursor.moveToFirst()) { + do { + students.add(studentsCursor.getInt(0) to studentsCursor.getString(1)) + } while (studentsCursor.moveToNext()) + } + return students + } + + private fun updateSemestersTable(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Semesters ADD COLUMN school_year INTEGER DEFAULT 1970 NOT NULL") + database.execSQL("ALTER TABLE Semesters ADD COLUMN start INTEGER DEFAULT 0 NOT NULL") + database.execSQL("ALTER TABLE Semesters ADD COLUMN `end` INTEGER DEFAULT 0 NOT NULL") + } + + private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List> { + val students = mutableListOf>() + val studentsCursor = database.query("SELECT student_id, class_id FROM Students") + if (studentsCursor.moveToFirst()) { + do { + students.add(studentsCursor.getInt(0) to studentsCursor.getInt(1)) + } while (studentsCursor.moveToNext()) + } + return students + } + + private fun markAtLeastAndOnlyOneSemesterAtCurrent(database: SupportSQLiteDatabase, students: List>) { + students.forEach { (studentId, classId) -> + database.execSQL("UPDATE Semesters SET is_current = 0 WHERE student_id = '$studentId' AND class_id = '$classId'") + database.execSQL("UPDATE Semesters SET is_current = 1 WHERE id = (SELECT id FROM Semesters WHERE student_id = '$studentId' AND class_id = '$classId' ORDER BY semester_id DESC)") + } + } + + private fun clearMessagesTable(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Messages") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration14.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration14.kt new file mode 100644 index 00000000..4dac0d30 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration14.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration14 : Migration(13, 14) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS GradesSummary") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS GradesSummary ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + semester_id INTEGER NOT NULL, + student_id INTEGER NOT NULL, + position INTEGER NOT NULL, + subject TEXT NOT NULL, + predicted_grade TEXT NOT NULL, + final_grade TEXT NOT NULL, + proposed_points TEXT NOT NULL, + final_points TEXT NOT NULL, + points_sum TEXT NOT NULL, + average REAL NOT NULL + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration15.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration15.kt new file mode 100644 index 00000000..5be49a95 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration15.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration15 : Migration(14, 15) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS MobileDevices ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + student_id INTEGER NOT NULL, + device_id INTEGER NOT NULL, + name TEXT NOT NULL, + date INTEGER NOT NULL + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt new file mode 100644 index 00000000..c5a30991 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration2 : Migration(1, 2) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS LuckyNumbers ( + id INTEGER PRIMARY KEY NOT NULL, + is_notified INTEGER NOT NULL, + student_id INTEGER NOT NULL, + date INTEGER NOT NULL, + lucky_number INTEGER NOT NULL) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt new file mode 100644 index 00000000..d9699c0f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration3 : Migration(2, 3) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS CompletedLesson ( + id INTEGER PRIMARY KEY NOT NULL, + student_id INTEGER NOT NULL, + diary_id INTEGER NOT NULL, + date INTEGER NOT NULL, + number INTEGER NOT NULL, + subject TEXT NOT NULL, + topic TEXT NOT NULL, + teacher TEXT NOT NULL, + teacher_symbol TEXT NOT NULL, + substitution TEXT NOT NULL, + absence TEXT NOT NULL, + resources TEXT NOT NULL) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt new file mode 100644 index 00000000..0ae89bdd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration4 : Migration(3, 4) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Messages") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Messages ( + id INTEGER PRIMARY KEY NOT NULL, + is_notified INTEGER NOT NULL, + content TEXT, + student_id INTEGER NOT NULL, + real_id INTEGER NOT NULL, + message_id INTEGER NOT NULL, + sender_name TEXT NOT NULL, + sender_id INTEGER NOT NULL, + recipient_id INTEGER NOT NULL, + recipient_name TEXT NOT NULL, + subject TEXT NOT NULL, + date INTEGER NOT NULL, + folder_id INTEGER NOT NULL, + unread INTEGER NOT NULL, + unreadBy INTEGER NOT NULL, + readBy INTEGER NOT NULL, + removed INTEGER NOT NULL) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt new file mode 100644 index 00000000..fe0dec48 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import org.threeten.bp.LocalDateTime.now +import org.threeten.bp.ZoneOffset + +class Migration5 : Migration(4, 5) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN registration_date INTEGER DEFAULT 0 NOT NULL") + database.execSQL("UPDATE Students SET registration_date = '${now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli()}'") + database.execSQL("DROP TABLE IF EXISTS Notes") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Notes ( + id INTEGER PRIMARY KEY NOT NULL, + is_read INTEGER NOT NULL, + is_notified INTEGER NOT NULL, + student_id INTEGER NOT NULL, + date INTEGER NOT NULL, + teacher TEXT NOT NULL, + category TEXT NOT NULL, + content TEXT NOT NULL) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt new file mode 100644 index 00000000..fa943618 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration6 : Migration(5, 6) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS ReportingUnits ( + id INTEGER PRIMARY KEY NOT NULL, + student_id INTEGER NOT NULL, + real_id INTEGER NOT NULL, + short TEXT NOT NULL, + sender_id INTEGER NOT NULL, + sender_name TEXT NOT NULL, + roles TEXT NOT NULL) + """) + + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Recipients ( + id INTEGER PRIMARY KEY NOT NULL, + student_id INTEGER NOT NULL, + real_id TEXT NOT NULL, + name TEXT NOT NULL, + real_name TEXT NOT NULL, + login_id INTEGER NOT NULL, + unit_id INTEGER NOT NULL, + role INTEGER NOT NULL, + hash TEXT NOT NULL) + """) + + database.execSQL("DELETE FROM Semesters WHERE 1") + database.execSQL("ALTER TABLE Semesters ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") + database.execSQL("ALTER TABLE Semesters ADD COLUMN unit_id INTEGER DEFAULT 0 NOT NULL") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt new file mode 100644 index 00000000..120716c8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration7 : Migration(6, 7) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS GradesStatistics ( + id INTEGER PRIMARY KEY NOT NULL, + student_id INTEGER NOT NULL, + semester_id INTEGER NOT NULL, + subject TEXT NOT NULL, + grade INTEGER NOT NULL, + amount INTEGER NOT NULL, + is_semester INTEGER NOT NULL) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt new file mode 100644 index 00000000..7009ee12 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration8 : Migration(7, 8) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Timetable ADD COLUMN subjectOld TEXT DEFAULT \"\" NOT NULL") + database.execSQL("ALTER TABLE Timetable ADD COLUMN roomOld TEXT DEFAULT \"\" NOT NULL") + database.execSQL("ALTER TABLE Timetable ADD COLUMN teacherOld TEXT DEFAULT \"\" NOT NULL") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt new file mode 100644 index 00000000..d79a5706 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration9 : Migration(8, 9) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Messages") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Messages ( + id INTEGER PRIMARY KEY NOT NULL, + student_id INTEGER NOT NULL, + real_id INTEGER NOT NULL, + message_id INTEGER NOT NULL, + sender_name TEXT NOT NULL, + sender_id INTEGER NOT NULL, + recipient_name TEXT NOT NULL, + subject TEXT NOT NULL, + date INTEGER NOT NULL, + folder_id INTEGER NOT NULL, + unread INTEGER NOT NULL, + unread_by INTEGER NOT NULL, + read_by INTEGER NOT NULL, + removed INTEGER NOT NULL, + is_notified INTEGER NOT NULL, + content TEXT) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java deleted file mode 100644 index 0e654997..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.data.db.resources; - -import javax.inject.Singleton; - -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; - -@Singleton -public interface ResourcesContract { - - String[] getSymbolsKeysArray(); - - String[] getSymbolsValuesArray(); - - String getErrorLoginMessage(Exception e); - - String getAttendanceLessonDescription(AttendanceLesson lesson); -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesRepository.java b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesRepository.java deleted file mode 100644 index f8a1baaa..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesRepository.java +++ /dev/null @@ -1,87 +0,0 @@ -package io.github.wulkanowy.data.db.resources; - -import android.content.Context; -import android.content.res.Resources; - -import java.io.IOException; -import java.net.SocketTimeoutException; -import java.net.UnknownHostException; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.R; -import io.github.wulkanowy.api.NotLoggedInErrorException; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; -import io.github.wulkanowy.utils.AppConstant; -import io.github.wulkanowy.utils.security.CryptoException; -import timber.log.Timber; - -@Singleton -public class ResourcesRepository implements ResourcesContract { - - private Resources resources; - - @Inject - ResourcesRepository(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) { - Timber.e(exception,"%s encountered a error", AppConstant.APP_NAME); - - 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_failed_text); - } else { - return exception.getMessage(); - } - } - - @Override - public String getAttendanceLessonDescription(AttendanceLesson lesson) { - int id = R.string.attendance_present; - - if (lesson.getAbsenceForSchoolReasons()) { - id = R.string.attendance_absence_for_school_reasons; - } - - if (lesson.getAbsenceExcused()) { - id = R.string.attendance_absence_excused; - } - - if (lesson.getAbsenceUnexcused()) { - id = R.string.attendance_absence_unexcused; - } - - if (lesson.getExemption()) { - id = R.string.attendance_exemption; - } - - if (lesson.getExcusedLateness()) { - id = R.string.attendance_excused_lateness; - } - - if (lesson.getUnexcusedLateness()) { - id = R.string.attendance_unexcused_lateness; - } - - return resources.getString(id); - } -} 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 deleted file mode 100644 index 370f8dfe..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefContract.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.data.db.shared; - -import javax.inject.Singleton; - -@Singleton -public interface SharedPrefContract { - - long getCurrentUserId(); - - boolean isUserLoggedIn(); - - void setCurrentUserId(long userId); - - void setTimetableWidgetState(boolean nextDay); - - boolean getTimetableWidgetState(); - - int getStartupTab(); - - boolean isShowGradesSummary(); - - boolean isShowAttendancePresent(); - - int getCurrentTheme(); - - int getServicesInterval(); - - boolean isMobileDisable(); - - boolean isServicesEnable(); - - boolean isNotifyEnable(); - - void cleanSharedPref(); -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefRepository.java b/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefRepository.java deleted file mode 100644 index adedbda4..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefRepository.java +++ /dev/null @@ -1,102 +0,0 @@ -package io.github.wulkanowy.data.db.shared; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -import io.github.wulkanowy.ui.main.settings.SettingsFragment; - -@Singleton -public class SharedPrefRepository implements SharedPrefContract { - - private static final String SHARED_KEY_USER_ID = "USER_ID"; - - private static final String SHARED_KEY_TIMETABLE_WIDGET_STATE = "TIMETABLE_WIDGET_STATE"; - - private final SharedPreferences appSharedPref; - - private final SharedPreferences settingsSharedPref; - - @Inject - SharedPrefRepository(Context context, @Named("sharedPrefName") String sharedName) { - appSharedPref = context.getSharedPreferences(sharedName, Context.MODE_PRIVATE); - settingsSharedPref = PreferenceManager.getDefaultSharedPreferences(context); - } - - @Override - public long getCurrentUserId() { - return appSharedPref.getLong(SHARED_KEY_USER_ID, 0); - } - - @Override - public boolean isUserLoggedIn() { - return getCurrentUserId() != 0; - } - - @Override - public void setCurrentUserId(long userId) { - appSharedPref.edit().putLong(SHARED_KEY_USER_ID, userId).apply(); - } - - @SuppressLint("ApplySharedPref") - @Override - public void setTimetableWidgetState(boolean nextDay) { - appSharedPref.edit().putBoolean(SHARED_KEY_TIMETABLE_WIDGET_STATE, nextDay).commit(); - } - - @Override - public boolean getTimetableWidgetState() { - return appSharedPref.getBoolean(SHARED_KEY_TIMETABLE_WIDGET_STATE, false); - } - - @Override - public int getStartupTab() { - return Integer.parseInt(settingsSharedPref.getString(SettingsFragment.SHARED_KEY_START_TAB, "0")); - } - - @Override - public boolean isShowGradesSummary() { - return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_GRADES_SUMMARY, false); - } - - @Override - public boolean isShowAttendancePresent() { - return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_ATTENDANCE_PRESENT, false); - } - - @Override - public int getCurrentTheme() { - return Integer.parseInt(settingsSharedPref.getString(SettingsFragment.SHARED_KEY_THEME, "1")); - } - - @Override - public int getServicesInterval() { - return Integer.parseInt(settingsSharedPref.getString(SettingsFragment.SHARED_KEY_SERVICES_INTERVAL, "60")); - } - - @Override - public boolean isServicesEnable() { - return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_SERVICES_ENABLE, true); - } - - @Override - public boolean isNotifyEnable() { - return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_NOTIFY_ENABLE, true); - } - - @Override - public boolean isMobileDisable() { - return settingsSharedPref.getBoolean(SettingsFragment.SHARED_KEY_SERVICES_MOBILE_DISABLED, false); - } - - @Override - public void cleanSharedPref() { - appSharedPref.edit().clear().apply(); - settingsSharedPref.edit().clear().apply(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt b/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt new file mode 100644 index 00000000..58a2396e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt @@ -0,0 +1,3 @@ +package io.github.wulkanowy.data.exceptions + +class NoCurrentStudentException : Exception("There no set current student in database") diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/MobileDeviceToken.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/MobileDeviceToken.kt new file mode 100644 index 00000000..40101821 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/MobileDeviceToken.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.pojos + +data class MobileDeviceToken( + + val token: String, + + val symbol: String, + + val pin: String, + + val qr: String +) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt new file mode 100644 index 00000000..0f587376 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.repositories.attendance + +import io.github.wulkanowy.data.db.dao.AttendanceDao +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Maybe +import org.threeten.bp.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDao) { + + fun saveAttendance(attendance: List) { + attendanceDb.insertAll(attendance) + } + + fun deleteAttendance(attendance: List) { + attendanceDb.deleteAll(attendance) + } + + fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { + return attendanceDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate).filter { it.isNotEmpty() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt new file mode 100644 index 00000000..b3544c3f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.data.repositories.attendance + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.utils.toLocalDate +import io.reactivex.Single +import org.threeten.bp.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AttendanceRemote @Inject constructor(private val api: Api) { + + fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { it.getAttendance(startDate, endDate) }.map { attendance -> + attendance.map { + Attendance( + studentId = semester.studentId, + diaryId = semester.diaryId, + date = it.date.toLocalDate(), + number = it.number, + subject = it.subject, + name = it.name, + presence = it.presence, + absence = it.absence, + exemption = it.exemption, + lateness = it.lateness, + excused = it.excused, + deleted = it.deleted + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt new file mode 100644 index 00000000..85102b3c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt @@ -0,0 +1,44 @@ +package io.github.wulkanowy.data.repositories.attendance + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.utils.friday +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.uniqueSubtract +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.uniqueSubtract(newAttendance)) + local.saveAttendance(newAttendance.uniqueSubtract(oldAttendance)) + } + }.flatMap { + local.getAttendance(semester, dates.first, dates.second) + .toSingle(emptyList()) + }).map { list -> list.filter { it.date in startDate..endDate } } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryLocal.kt new file mode 100644 index 00000000..2e9a1006 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryLocal.kt @@ -0,0 +1,24 @@ +package io.github.wulkanowy.data.repositories.attendancesummary + +import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Maybe +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AttendanceSummaryLocal @Inject constructor(private val attendanceDb: AttendanceSummaryDao) { + + fun saveAttendanceSummary(attendance: List) { + attendanceDb.insertAll(attendance) + } + + fun deleteAttendanceSummary(attendance: List) { + attendanceDb.deleteAll(attendance) + } + + fun getAttendanceSummary(semester: Semester, subjectId: Int): Maybe> { + return attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId).filter { it.isNotEmpty() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt new file mode 100644 index 00000000..d38dd3a4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.data.repositories.attendancesummary + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AttendanceSummaryRemote @Inject constructor(private val api: Api) { + + fun getAttendanceSummary(semester: Semester, subjectId: Int): Single> { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { api.getAttendanceSummary(subjectId) }.map { attendance -> + attendance.map { + AttendanceSummary( + studentId = semester.studentId, + diaryId = semester.diaryId, + subjectId = subjectId, + month = it.month, + presence = it.presence, + absence = it.absence, + absenceExcused = it.absenceExcused, + absenceForSchoolReasons = it.absenceForSchoolReasons, + lateness = it.lateness, + latenessExcused = it.latenessExcused, + exemption = it.exemption + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt new file mode 100644 index 00000000..c6587050 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.data.repositories.attendancesummary + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.utils.uniqueSubtract +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.uniqueSubtract(new)) + local.saveAttendanceSummary(new.uniqueSubtract(old)) + } + }.flatMap { local.getAttendanceSummary(semester, subjectId).toSingle(emptyList()) }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocal.kt new file mode 100644 index 00000000..9b275908 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocal.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.repositories.completedlessons + +import io.github.wulkanowy.data.db.dao.CompletedLessonsDao +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Maybe +import org.threeten.bp.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class CompletedLessonsLocal @Inject constructor(private val completedLessonsDb: CompletedLessonsDao) { + + fun saveCompletedLessons(completedLessons: List) { + completedLessonsDb.insertAll(completedLessons) + } + + fun deleteCompleteLessons(completedLessons: List) { + completedLessonsDb.deleteAll(completedLessons) + } + + fun getCompletedLessons(semester: Semester, start: LocalDate, end: LocalDate): Maybe> { + return completedLessonsDb.loadAll(semester.diaryId, semester.studentId, start, end).filter { it.isNotEmpty() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt new file mode 100644 index 00000000..58dd5a9d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.data.repositories.completedlessons + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.api.toLocalDate +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Single +import org.threeten.bp.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class CompletedLessonsRemote @Inject constructor(private val api: Api) { + + fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { it.getCompletedLessons(startDate, endDate) } + .map { lessons -> + lessons.map { + it.absence + CompletedLesson( + studentId = semester.studentId, + diaryId = semester.diaryId, + date = it.date.toLocalDate(), + number = it.number, + subject = it.subject, + topic = it.topic, + teacher = it.teacher, + teacherSymbol = it.teacherSymbol, + substitution = it.substitution, + absence = it.absence, + resources = it.resources + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt new file mode 100644 index 00000000..c22fabc3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt @@ -0,0 +1,44 @@ +package io.github.wulkanowy.data.repositories.completedlessons + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.utils.friday +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.uniqueSubtract +import io.reactivex.Single +import org.threeten.bp.LocalDate +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class CompletedLessonsRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: CompletedLessonsLocal, + private val remote: CompletedLessonsRemote +) { + + fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single> { + return Single.fromCallable { startDate.monday to endDate.friday } + .flatMap { dates -> + local.getCompletedLessons(semester, dates.first, dates.second).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getCompletedLessons(semester, dates.first, dates.second) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getCompletedLessons(semester, dates.first, dates.second) + .toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteCompleteLessons(old.uniqueSubtract(new)) + local.saveCompletedLessons(new.uniqueSubtract(old)) + } + }.flatMap { + local.getCompletedLessons(semester, dates.first, dates.second) + .toSingle(emptyList()) + }).map { list -> list.filter { it.date in startDate..endDate } } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamLocal.kt new file mode 100644 index 00000000..0f484d32 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamLocal.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.repositories.exam + +import io.github.wulkanowy.data.db.dao.ExamDao +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Maybe +import org.threeten.bp.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ExamLocal @Inject constructor(private val examDb: ExamDao) { + + fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { + return examDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate) + .filter { !it.isEmpty() } + } + + fun saveExams(exams: List) { + examDb.insertAll(exams) + } + + fun deleteExams(exams: List) { + examDb.deleteAll(exams) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt new file mode 100644 index 00000000..f6d653a6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.data.repositories.exam + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.utils.toLocalDate +import io.reactivex.Single +import org.threeten.bp.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ExamRemote @Inject constructor(private val api: Api) { + + fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { it.getExams(startDate, endDate) }.map { exams -> + exams.map { + Exam( + studentId = semester.studentId, + diaryId = semester.diaryId, + date = it.date.toLocalDate(), + entryDate = it.entryDate.toLocalDate(), + subject = it.subject, + group = it.group, + type = it.type, + description = it.description, + teacher = it.teacher, + teacherSymbol = it.teacherSymbol + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt new file mode 100644 index 00000000..be60eaec --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt @@ -0,0 +1,44 @@ +package io.github.wulkanowy.data.repositories.exam + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.utils.friday +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.uniqueSubtract +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 { new -> + local.getExams(semester, dates.first, dates.second) + .toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteExams(old.uniqueSubtract(new)) + local.saveExams(new.uniqueSubtract(old)) + } + }.flatMap { + local.getExams(semester, dates.first, dates.second) + .toSingle(emptyList()) + }).map { list -> list.filter { it.date in startDate..endDate } } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeLocal.kt new file mode 100644 index 00000000..4983a474 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeLocal.kt @@ -0,0 +1,28 @@ +package io.github.wulkanowy.data.repositories.grade + +import io.github.wulkanowy.data.db.dao.GradeDao +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Maybe +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GradeLocal @Inject constructor(private val gradeDb: GradeDao) { + + fun saveGrades(grades: List) { + gradeDb.insertAll(grades) + } + + fun deleteGrades(grades: List) { + gradeDb.deleteAll(grades) + } + + fun updateGrades(grades: List) { + gradeDb.updateAll(grades) + } + + fun getGrades(semester: Semester): Maybe> { + return gradeDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt new file mode 100644 index 00000000..570ab7a7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.data.repositories.grade + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.utils.toLocalDate +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GradeRemote @Inject constructor(private val api: Api) { + + fun getGrades(semester: Semester): Single> { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { it.getGrades(semester.semesterId) } + .map { grades -> + grades.map { + Grade( + semesterId = semester.semesterId, + studentId = semester.studentId, + subject = it.subject, + entry = it.entry, + value = it.value, + modifier = it.modifier, + comment = it.comment, + color = it.color, + gradeSymbol = it.symbol.orEmpty(), + description = it.description.orEmpty(), + weight = it.weight, + weightValue = it.weightValue, + date = it.date.toLocalDate(), + teacher = it.teacher + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt new file mode 100644 index 00000000..7f8e92a2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt @@ -0,0 +1,59 @@ +package io.github.wulkanowy.data.repositories.grade + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.utils.uniqueSubtract +import io.reactivex.Completable +import io.reactivex.Single +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GradeRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: GradeLocal, + private val remote: GradeRemote +) { + + fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Single> { + return local.getGrades(semester).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getGrades(semester) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getGrades(semester).toSingle(emptyList()) + .doOnSuccess { old -> + val notifyBreakDate = old.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate() + local.deleteGrades(old.uniqueSubtract(new)) + local.saveGrades(new.uniqueSubtract(old) + .onEach { + if (it.date >= notifyBreakDate) it.apply { + isRead = false + if (notify) isNotified = false + } + }) + } + }.flatMap { local.getGrades(semester).toSingle(emptyList()) }) + } + + fun getUnreadGrades(semester: Semester): Single> { + return local.getGrades(semester).map { it.filter { grade -> !grade.isRead } }.toSingle(emptyList()) + } + + fun getNotNotifiedGrades(semester: Semester): Single> { + return local.getGrades(semester).map { it.filter { grade -> !grade.isNotified } }.toSingle(emptyList()) + } + + fun updateGrade(grade: Grade): Completable { + return Completable.fromCallable { local.updateGrades(listOf(grade)) } + } + + fun updateGrades(grades: List): Completable { + return Completable.fromCallable { local.updateGrades(grades) } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryLocal.kt new file mode 100644 index 00000000..e74641d3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryLocal.kt @@ -0,0 +1,24 @@ +package io.github.wulkanowy.data.repositories.gradessummary + +import io.github.wulkanowy.data.db.dao.GradeSummaryDao +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Maybe +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GradeSummaryLocal @Inject constructor(private val gradeSummaryDb: GradeSummaryDao) { + + fun saveGradesSummary(gradesSummary: List) { + gradeSummaryDb.insertAll(gradesSummary) + } + + fun deleteGradesSummary(gradesSummary: List) { + gradeSummaryDb.deleteAll(gradesSummary) + } + + fun getGradesSummary(semester: Semester): Maybe> { + return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt new file mode 100644 index 00000000..3335cfb0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.data.repositories.gradessummary + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GradeSummaryRemote @Inject constructor(private val api: Api) { + + fun getGradeSummary(semester: Semester): Single> { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { it.getGradesSummary(semester.semesterId) } + .map { gradesSummary -> + gradesSummary.map { + GradeSummary( + semesterId = semester.semesterId, + studentId = semester.studentId, + position = it.order, + subject = it.name, + predictedGrade = it.predicted, + finalGrade = it.final, + pointsSum = it.pointsSum, + proposedPoints = it.proposedPoints, + finalPoints = it.finalPoints, + average = it.average + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt new file mode 100644 index 00000000..660a032c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.data.repositories.gradessummary + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.utils.uniqueSubtract +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 { new -> + local.getGradesSummary(semester).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteGradesSummary(old.uniqueSubtract(new)) + local.saveGradesSummary(new.uniqueSubtract(old)) + } + }.flatMap { local.getGradesSummary(semester).toSingle(emptyList()) }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt new file mode 100644 index 00000000..581ac2f8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.data.repositories.gradestatistics + +import io.github.wulkanowy.data.db.dao.GradeStatisticsDao +import io.github.wulkanowy.data.db.entities.GradeStatistics +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Maybe +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GradeStatisticsLocal @Inject constructor(private val gradeStatisticsDb: GradeStatisticsDao) { + + fun getGradesStatistics(semester: Semester, isSemester: Boolean): Maybe> { + return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester) + .filter { !it.isEmpty() } + } + + fun getGradesStatistics(semester: Semester, isSemester: Boolean, subjectName: String): Maybe> { + return (if ("Wszystkie" == subjectName) gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester).map { list -> + list.groupBy { it.grade }.map { + GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key, it.value.fold(0) { acc, e -> acc + e.amount }, false) + } + } + else gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)).filter { !it.isEmpty() } + } + + fun saveGradesStatistics(gradesStatistics: List) { + gradeStatisticsDb.insertAll(gradesStatistics) + } + + fun deleteGradesStatistics(gradesStatistics: List) { + gradeStatisticsDb.deleteAll(gradesStatistics) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt new file mode 100644 index 00000000..fa3b951f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.data.repositories.gradestatistics + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.GradeStatistics +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GradeStatisticsRemote @Inject constructor(private val api: Api) { + + fun getGradeStatistics(semester: Semester, isSemester: Boolean): Single> { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { it.getGradesStatistics(semester.semesterId, isSemester) } + .map { gradeStatistics -> + gradeStatistics.map { + GradeStatistics( + semesterId = semester.semesterId, + studentId = semester.studentId, + subject = it.subject, + grade = it.gradeValue, + amount = it.amount ?: 0, + semester = isSemester + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt new file mode 100644 index 00000000..9617cdd6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.data.repositories.gradestatistics + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.db.entities.GradeStatistics +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.utils.uniqueSubtract +import io.reactivex.Single +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GradeStatisticsRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: GradeStatisticsLocal, + private val remote: GradeStatisticsRemote +) { + + fun getGradesStatistics(semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single> { + return local.getGradesStatistics(semester, isSemester, subjectName).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getGradeStatistics(semester, isSemester) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getGradesStatistics(semester, isSemester).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteGradesStatistics(old.uniqueSubtract(new)) + local.saveGradesStatistics(new.uniqueSubtract(old)) + } + }.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).toSingle(emptyList()) }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkLocal.kt new file mode 100644 index 00000000..671ecafd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkLocal.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.repositories.homework + +import io.github.wulkanowy.data.db.dao.HomeworkDao +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Maybe +import org.threeten.bp.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) { + + fun saveHomework(homework: List) { + homeworkDb.insertAll(homework) + } + + fun deleteHomework(homework: List) { + homeworkDb.deleteAll(homework) + } + + fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { + return homeworkDb.loadAll(semester.semesterId, semester.studentId, startDate, endDate) + .filter { it.isNotEmpty() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt new file mode 100644 index 00000000..681b6646 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.data.repositories.homework + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.utils.toLocalDate +import io.reactivex.Single +import org.threeten.bp.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class HomeworkRemote @Inject constructor(private val api: Api) { + + fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { it.getHomework(startDate, endDate) } + .map { homework -> + homework.map { + Homework( + semesterId = semester.semesterId, + studentId = semester.studentId, + date = it.date.toLocalDate(), + entryDate = it.entryDate.toLocalDate(), + subject = it.subject, + content = it.content, + teacher = it.teacher, + teacherSymbol = it.teacherSymbol + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt new file mode 100644 index 00000000..3f924944 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.data.repositories.homework + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.utils.friday +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.uniqueSubtract +import io.reactivex.Single +import org.threeten.bp.LocalDate +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class HomeworkRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: HomeworkLocal, + private val remote: HomeworkRemote +) { + + fun getHomework(semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single> { + return Single.fromCallable { start.monday to end.friday }.flatMap { (monday, friday) -> + local.getHomework(semester, monday, friday).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getHomework(semester, monday, friday) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getHomework(semester, monday, friday).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteHomework(old.uniqueSubtract(new)) + local.saveHomework(new.uniqueSubtract(old)) + } + }.flatMap { local.getHomework(semester, monday, friday).toSingle(emptyList()) }) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt new file mode 100644 index 00000000..115c8965 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.data.repositories.luckynumber + +import io.github.wulkanowy.data.db.dao.LuckyNumberDao +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Maybe +import org.threeten.bp.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumberDao) { + + fun saveLuckyNumber(luckyNumber: LuckyNumber) { + luckyNumberDb.insert(luckyNumber) + } + + fun updateLuckyNumber(luckyNumber: LuckyNumber) { + luckyNumberDb.update(luckyNumber) + } + + fun deleteLuckyNumber(luckyNumber: LuckyNumber) { + luckyNumberDb.delete(luckyNumber) + } + + fun getLuckyNumber(semester: Semester, date: LocalDate): Maybe { + return luckyNumberDb.load(semester.studentId, date) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt new file mode 100644 index 00000000..1b0f12b3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.repositories.luckynumber + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Maybe +import io.reactivex.Single +import org.threeten.bp.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class LuckyNumberRemote @Inject constructor(private val api: Api) { + + fun getLuckyNumber(semester: Semester): Maybe { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMapMaybe { it.getLuckyNumber() } + .map { + LuckyNumber( + studentId = semester.studentId, + date = LocalDate.now(), + luckyNumber = it + ) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt new file mode 100644 index 00000000..4036521f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.data.repositories.luckynumber + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Completable +import io.reactivex.Maybe +import org.threeten.bp.LocalDate +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class LuckyNumberRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: LuckyNumberLocal, + private val remote: LuckyNumberRemote +) { + + fun getLuckyNumber(semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Maybe { + return local.getLuckyNumber(semester, LocalDate.now()).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMapMaybe { + if (it) remote.getLuckyNumber(semester) + else Maybe.error(UnknownHostException()) + }.flatMap { new -> + local.getLuckyNumber(semester, LocalDate.now()) + .doOnSuccess { old -> + if (new != old) { + local.deleteLuckyNumber(old) + local.saveLuckyNumber(new.apply { + if (notify) isNotified = false + }) + } + } + .doOnComplete { + local.saveLuckyNumber(new.apply { + if (notify) isNotified = false + }) + } + }.flatMap({ local.getLuckyNumber(semester, LocalDate.now()) }, { Maybe.error(it) }, + { local.getLuckyNumber(semester, LocalDate.now()) }) + ) + } + + fun getNotNotifiedLuckyNumber(semester: Semester): Maybe { + return local.getLuckyNumber(semester, LocalDate.now()).filter { !it.isNotified } + } + + fun updateLuckyNumber(luckyNumber: LuckyNumber): Completable { + return Completable.fromCallable { local.updateLuckyNumber(luckyNumber) } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt new file mode 100644 index 00000000..06f5a1e0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt @@ -0,0 +1,7 @@ +package io.github.wulkanowy.data.repositories.message + +enum class MessageFolder(val id: Int = 1) { + RECEIVED(1), + SENT(2), + TRASHED(3) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt new file mode 100644 index 00000000..ce08d13e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.data.repositories.message + +import io.github.wulkanowy.data.db.dao.MessagesDao +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED +import io.reactivex.Maybe +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) { + + fun saveMessages(messages: List) { + messagesDb.insertAll(messages) + } + + fun updateMessages(messages: List) { + messagesDb.updateAll(messages) + } + + fun deleteMessages(messages: List) { + messagesDb.deleteAll(messages) + } + + fun getMessage(id: Long): Maybe { + return messagesDb.load(id) + } + + fun getMessages(student: Student, folder: MessageFolder): Maybe> { + return when (folder) { + TRASHED -> messagesDb.loadDeleted(student.id.toInt()) + else -> messagesDb.loadAll(student.id.toInt(), folder.id) + }.filter { it.isNotEmpty() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt new file mode 100644 index 00000000..b2bff1e7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt @@ -0,0 +1,66 @@ +package io.github.wulkanowy.data.repositories.message + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.api.messages.Folder +import io.github.wulkanowy.api.messages.SentMessage +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.utils.toLocalDateTime +import io.reactivex.Single +import org.threeten.bp.LocalDateTime.now +import javax.inject.Inject +import javax.inject.Singleton +import io.github.wulkanowy.api.messages.Message as ApiMessage +import io.github.wulkanowy.api.messages.Recipient as ApiRecipient + +@Singleton +class MessageRemote @Inject constructor(private val api: Api) { + + fun getMessages(student: Student, folder: MessageFolder): Single> { + return api.getMessages(Folder.valueOf(folder.name)).map { messages -> + messages.map { + Message( + studentId = student.id.toInt(), + realId = it.id ?: 0, + messageId = it.messageId ?: 0, + sender = it.sender.orEmpty(), + senderId = it.senderId ?: 0, + recipient = it.recipient.orEmpty(), + subject = it.subject.trim(), + date = it.date?.toLocalDateTime() ?: now(), + folderId = it.folderId, + unread = it.unread ?: false, + unreadBy = it.unreadBy ?: 0, + readBy = it.readBy ?: 0, + removed = it.removed + ) + } + } + } + + fun getMessagesContent(message: Message, markAsRead: Boolean = false): Single { + return api.getMessageContent(message.messageId, message.folderId, markAsRead, message.realId) + } + + fun sendMessage(subject: String, content: String, recipients: List): Single { + return api.sendMessage( + subject = subject, + content = content, + recipients = recipients.map { + ApiRecipient( + id = it.realId, + name = it.realName, + loginId = it.loginId, + reportingUnitId = it.unitId, + role = it.role, + hash = it.hash + ) + } + ) + } + + fun deleteMessage(message: Message): Single { + return api.deleteMessages(listOf(Pair(message.realId, message.folderId))) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt new file mode 100644 index 00000000..c10cd518 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt @@ -0,0 +1,110 @@ +package io.github.wulkanowy.data.repositories.message + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.api.messages.SentMessage +import io.github.wulkanowy.data.ApiHelper +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED +import io.github.wulkanowy.utils.uniqueSubtract +import io.reactivex.Completable +import io.reactivex.Maybe +import io.reactivex.Single +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class MessageRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: MessageLocal, + private val remote: MessageRemote, + private val apiHelper: ApiHelper +) { + + fun getMessages(student: Student, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): Single> { + return Single.just(apiHelper.initApi(student)) + .flatMap { _ -> + local.getMessages(student, folder).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getMessages(student, folder) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getMessages(student, folder).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteMessages(old.uniqueSubtract(new)) + local.saveMessages(new.uniqueSubtract(old) + .onEach { + it.isNotified = !notify + }) + } + }.flatMap { local.getMessages(student, folder).toSingle(emptyList()) } + ) + } + } + + fun getMessage(student: Student, messageDbId: Long, markAsRead: Boolean = false): Single { + return Single.just(apiHelper.initApi(student)) + .flatMap { _ -> + local.getMessage(messageDbId) + .filter { !it.content.isNullOrEmpty() } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) local.getMessage(messageDbId).toSingle() + else Single.error(UnknownHostException()) + } + .flatMap { dbMessage -> + remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess { + local.updateMessages(listOf(dbMessage.copy(unread = false).apply { + id = dbMessage.id + content = it + })) + } + }.flatMap { + local.getMessage(messageDbId).toSingle() + } + ) + } + } + + fun getNotNotifiedMessages(student: Student): Single> { + return local.getMessages(student, RECEIVED) + .map { it.filter { message -> !message.isNotified && message.unread } } + .toSingle(emptyList()) + } + + fun updateMessage(message: Message): Completable { + return Completable.fromCallable { local.updateMessages(listOf(message)) } + } + + fun updateMessages(messages: List): Completable { + return Completable.fromCallable { local.updateMessages(messages) } + } + + fun sendMessage(subject: String, content: String, recipients: List): Single { + return ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.sendMessage(subject, content, recipients) + else Single.error(UnknownHostException()) + } + } + + fun deleteMessage(message: Message): Maybe { + return ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.deleteMessage(message) + else Single.error(UnknownHostException()) + } + .filter { it } + .doOnSuccess { + if (!message.removed) local.updateMessages(listOf(message.copy(removed = true).apply { + id = message.id + content = message.content + })) + else local.deleteMessages(listOf(message)) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceLocal.kt new file mode 100644 index 00000000..8f0efa5b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceLocal.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.repositories.mobiledevice + +import io.github.wulkanowy.data.db.dao.MobileDeviceDao +import io.github.wulkanowy.data.db.entities.MobileDevice +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 MobileDeviceLocal @Inject constructor(private val mobileDb: MobileDeviceDao) { + + fun saveDevices(devices: List) { + mobileDb.insertAll(devices) + } + + fun deleteDevices(devices: List) { + mobileDb.deleteAll(devices) + } + + fun getDevices(semester: Semester): Maybe> { + return mobileDb.loadAll(semester.studentId).filter { it.isNotEmpty() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRemote.kt new file mode 100644 index 00000000..86fdce97 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRemote.kt @@ -0,0 +1,47 @@ +package io.github.wulkanowy.data.repositories.mobiledevice + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.MobileDevice +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.pojos.MobileDeviceToken +import io.github.wulkanowy.utils.toLocalDateTime +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class MobileDeviceRemote @Inject constructor(private val api: Api) { + + fun getDevices(semester: Semester): Single> { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { api.getRegisteredDevices() } + .map { devices -> + devices.map { + MobileDevice( + studentId = semester.studentId, + date = it.date.toLocalDateTime(), + deviceId = it.id, + name = it.name + ) + } + } + } + + fun unregisterDevice(semester: Semester, device: MobileDevice): Single { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { api.unregisterDevice(device.deviceId) } + } + + fun getToken(semester: Semester): Single { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { api.getToken() } + .map { + MobileDeviceToken( + token = it.token, + symbol = it.symbol, + pin = it.pin, + qr = it.qrCodeImage + ) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRepository.kt new file mode 100644 index 00000000..3643a701 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRepository.kt @@ -0,0 +1,44 @@ +package io.github.wulkanowy.data.repositories.mobiledevice + +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.MobileDevice +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.pojos.MobileDeviceToken +import io.github.wulkanowy.utils.uniqueSubtract +import io.reactivex.Single +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class MobileDeviceRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: MobileDeviceLocal, + private val remote: MobileDeviceRemote +) { + + fun getDevices(semester: Semester, forceRefresh: Boolean = false): Single> { + return local.getDevices(semester).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getDevices(semester) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getDevices(semester).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteDevices(old uniqueSubtract new) + local.saveDevices(new uniqueSubtract old) + } + } + ).flatMap { local.getDevices(semester).toSingle(emptyList()) } + } + + fun unregisterDevice(semester: Semester, device: MobileDevice): Single { + return remote.unregisterDevice(semester, device) + } + + fun getToken(semester: Semester): Single { + return remote.getToken(semester) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt new file mode 100644 index 00000000..784e61f0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt @@ -0,0 +1,28 @@ +package io.github.wulkanowy.data.repositories.note + +import io.github.wulkanowy.data.db.dao.NoteDao +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.db.entities.Student +import io.reactivex.Maybe +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class NoteLocal @Inject constructor(private val noteDb: NoteDao) { + + fun saveNotes(notes: List) { + noteDb.insertAll(notes) + } + + fun updateNotes(notes: List) { + noteDb.updateAll(notes) + } + + fun deleteNotes(notes: List) { + noteDb.deleteAll(notes) + } + + fun getNotes(student: Student): Maybe> { + return noteDb.loadAll(student.studentId).filter { it.isNotEmpty() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt new file mode 100644 index 00000000..aebc6230 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.data.repositories.note + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.utils.toLocalDate +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class NoteRemote @Inject constructor(private val api: Api) { + + fun getNotes(semester: Semester): Single> { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { it.getNotes() } + .map { notes -> + notes.map { + Note( + studentId = semester.studentId, + date = it.date.toLocalDate(), + teacher = it.teacher, + category = it.category, + content = it.content + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt new file mode 100644 index 00000000..52cb2d0f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.data.repositories.note + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.utils.uniqueSubtract +import io.reactivex.Completable +import io.reactivex.Single +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class NoteRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: NoteLocal, + private val remote: NoteRemote +) { + + fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Single> { + return local.getNotes(student).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getNotes(semester) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getNotes(student).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteNotes(old.uniqueSubtract(new)) + local.saveNotes(new.uniqueSubtract(old) + .onEach { + if (it.date >= student.registrationDate.toLocalDate()) it.apply { + isRead = false + if (notify) isNotified = false + } + }) + } + }.flatMap { local.getNotes(student).toSingle(emptyList()) }) + } + + fun getNotNotifiedNotes(student: Student): Single> { + return local.getNotes(student).map { it.filter { note -> !note.isNotified } }.toSingle(emptyList()) + } + + fun updateNote(note: Note): Completable { + return Completable.fromCallable { local.updateNotes(listOf(note)) } + } + + fun updateNotes(notes: List): Completable { + return Completable.fromCallable { local.updateNotes(notes) } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt new file mode 100644 index 00000000..bde84162 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt @@ -0,0 +1,60 @@ +package io.github.wulkanowy.data.repositories.preferences + +import android.content.Context +import android.content.SharedPreferences +import io.github.wulkanowy.R +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class PreferencesRepository @Inject constructor( + private val sharedPref: SharedPreferences, + val context: Context +) { + val startMenuIndex: Int + get() = sharedPref.getString(context.getString(R.string.pref_key_start_menu), "0")?.toIntOrNull() ?: 0 + + val isShowPresent: Boolean + get() = sharedPref.getBoolean(context.getString(R.string.pref_key_attendance_present), true) + + val gradeAverageMode: String + get() = sharedPref.getString(context.getString(R.string.pref_key_grade_average_mode), "only_one_semester") ?: "only_one_semester" + + val isGradeExpandable: Boolean + get() = !sharedPref.getBoolean(context.getString(R.string.pref_key_expand_grade), false) + + val appThemeKey: String = context.getString(R.string.pref_key_app_theme) + val appTheme: String + get() = sharedPref.getString(appThemeKey, "light") ?: "light" + + val gradeColorTheme: String + get() = sharedPref.getString(context.getString(R.string.pref_key_grade_color_scheme), "vulcan") ?: "vulcan" + + val serviceEnableKey: String = context.getString(R.string.pref_key_services_enable) + val isServiceEnabled: Boolean + get() = sharedPref.getBoolean(serviceEnableKey, true) + + val servicesIntervalKey: String = context.getString(R.string.pref_key_services_interval) + val servicesInterval: Long + get() = sharedPref.getString(servicesIntervalKey, "60")?.toLongOrNull() ?: 60 + + val servicesOnlyWifiKey: String = context.getString(R.string.pref_key_services_wifi_only) + val isServicesOnlyWifi: Boolean + get() = sharedPref.getBoolean(servicesOnlyWifiKey, false) + + val isNotificationsEnable: Boolean + get() = sharedPref.getBoolean(context.getString(R.string.pref_key_notifications_enable), true) + + val isDebugNotificationEnableKey: String = context.getString(R.string.pref_key_notification_debug) + val isDebugNotificationEnable: Boolean + get() = sharedPref.getBoolean(isDebugNotificationEnableKey, false) + + val gradePlusModifier: Double + get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0 + + val gradeMinusModifier: Double + get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble() ?: 0.0 + + val fillMessageContent: Boolean + get() = sharedPref.getBoolean(context.getString(R.string.pref_key_fill_message_content), true) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt new file mode 100644 index 00000000..6b8328ec --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.repositories.recipient + +import io.github.wulkanowy.data.db.dao.RecipientDao +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.reactivex.Maybe +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RecipientLocal @Inject constructor(private val recipientDb: RecipientDao) { + + fun getRecipients(student: Student, role: Int, unit: ReportingUnit): Maybe> { + return recipientDb.load(student.studentId, role, unit.realId).filter { !it.isEmpty() } + } + + fun saveRecipients(recipients: List) { + return recipientDb.insertAll(recipients) + } + + fun deleteRecipients(recipients: List) { + recipientDb.deleteAll(recipients) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt new file mode 100644 index 00000000..b726edda --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt @@ -0,0 +1,41 @@ +package io.github.wulkanowy.data.repositories.recipient + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton +import io.github.wulkanowy.api.messages.Recipient as ApiRecipient + +@Singleton +class RecipientRemote @Inject constructor(private val api: Api) { + + fun getRecipients(role: Int, unit: ReportingUnit): Single> { + return api.getRecipients(unit.realId, role) + .map { recipients -> + recipients.map { it.toRecipient() } + } + } + + fun getMessageRecipients(message: Message): Single> { + return api.getMessageRecipients(message.messageId, message.senderId) + .map { recipients -> + recipients.map { it.toRecipient() } + } + } + + private fun ApiRecipient.toRecipient(): Recipient { + return Recipient( + studentId = api.studentId, + realId = id, + realName = name, + name = shortName, + hash = hash, + loginId = loginId, + role = role, + unitId = reportingUnitId ?: 0 + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt new file mode 100644 index 00000000..cde75ea8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.data.repositories.recipient + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.ApiHelper +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.utils.uniqueSubtract +import io.reactivex.Single +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RecipientRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: RecipientLocal, + private val remote: RecipientRemote, + private val apiHelper: ApiHelper +) { + + fun getRecipients(student: Student, role: Int, unit: ReportingUnit, forceRefresh: Boolean = false): Single> { + return Single.just(apiHelper.initApi(student)) + .flatMap { _ -> + local.getRecipients(student, role, unit).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getRecipients(role, unit) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getRecipients(student, role, unit).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteRecipients(old.uniqueSubtract(new)) + local.saveRecipients(new.uniqueSubtract(old)) + } + }.flatMap { + local.getRecipients(student, role, unit).toSingle(emptyList()) + } + ) + } + } + + fun getMessageRecipients(student: Student, message: Message): Single> { + return Single.just(apiHelper.initApi(student)) + .flatMap { ReactiveNetwork.checkInternetConnectivity(settings) } + .flatMap { + if (it) remote.getMessageRecipients(message) + else Single.error(UnknownHostException()) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt new file mode 100644 index 00000000..b4281cbf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt @@ -0,0 +1,28 @@ +package io.github.wulkanowy.data.repositories.reportingunit + +import io.github.wulkanowy.data.db.dao.ReportingUnitDao +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.reactivex.Maybe +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ReportingUnitLocal @Inject constructor(private val reportingUnitDb: ReportingUnitDao) { + + fun getReportingUnits(student: Student): Maybe> { + return reportingUnitDb.load(student.studentId).filter { !it.isEmpty() } + } + + fun getReportingUnit(student: Student, unitId: Int): Maybe { + return reportingUnitDb.loadOne(student.studentId, unitId) + } + + fun saveReportingUnits(reportingUnits: List) { + return reportingUnitDb.insertAll(reportingUnits) + } + + fun deleteReportingUnits(reportingUnits: List) { + reportingUnitDb.deleteAll(reportingUnits) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt new file mode 100644 index 00000000..feb4b013 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.repositories.reportingunit + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ReportingUnitRemote @Inject constructor(private val api: Api) { + + fun getReportingUnits(): Single> { + return api.getReportingUnits().map { + it.map { unit -> + ReportingUnit( + studentId = api.studentId, + realId = unit.id, + roles = unit.roles, + senderId = unit.senderId, + senderName = unit.senderName, + shortName = unit.short + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt new file mode 100644 index 00000000..6758898e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt @@ -0,0 +1,56 @@ +package io.github.wulkanowy.data.repositories.reportingunit + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.ApiHelper +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.utils.uniqueSubtract +import io.reactivex.Maybe +import io.reactivex.Single +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ReportingUnitRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: ReportingUnitLocal, + private val remote: ReportingUnitRemote, + private val apiHelper: ApiHelper +) { + + fun getReportingUnits(student: Student, forceRefresh: Boolean = false): Single> { + return Single.just(apiHelper.initApi(student)) + .flatMap { _ -> + local.getReportingUnits(student).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getReportingUnits() + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getReportingUnits(student).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteReportingUnits(old.uniqueSubtract(new)) + local.saveReportingUnits(new.uniqueSubtract(old)) + } + }.flatMap { local.getReportingUnits(student).toSingle(emptyList()) } + ) + } + } + + fun getReportingUnit(student: Student, unitId: Int): Maybe { + return Maybe.just(apiHelper.initApi(student)) + .flatMap { _ -> + local.getReportingUnit(student, unitId) + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) getReportingUnits(student, true) + else Single.error(UnknownHostException()) + }.flatMapMaybe { + local.getReportingUnit(student, unitId) + } + ) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt new file mode 100644 index 00000000..b9750e7d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt @@ -0,0 +1,24 @@ +package io.github.wulkanowy.data.repositories.semester + +import io.github.wulkanowy.data.db.dao.SemesterDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.reactivex.Maybe +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) { + + fun saveSemesters(semesters: List) { + semesterDb.insertAll(semesters) + } + + fun deleteSemesters(semesters: List) { + semesterDb.deleteAll(semesters) + } + + fun getSemesters(student: Student): Maybe> { + return semesterDb.loadAll(student.studentId, student.classId).filter { !it.isEmpty() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt new file mode 100644 index 00000000..c199c16c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt @@ -0,0 +1,35 @@ +package io.github.wulkanowy.data.repositories.semester + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SemesterRemote @Inject constructor(private val api: Api) { + + fun getSemesters(student: Student): Single> { + return api.getSemesters().map { semesters -> + semesters.map { semester -> + Semester( + studentId = student.studentId, + diaryId = semester.diaryId, + diaryName = semester.diaryName, + schoolYear = semester.schoolYear, + semesterId = semester.semesterId, + semesterName = semester.semesterNumber, + isCurrent = semester.current, + start = semester.start, + end = semester.end, + classId = semester.classId, + unitId = semester.unitId + ) + } + + } + } +} + + diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt new file mode 100644 index 00000000..59301403 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt @@ -0,0 +1,48 @@ +package io.github.wulkanowy.data.repositories.semester + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.ApiHelper +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.utils.uniqueSubtract +import io.reactivex.Maybe +import io.reactivex.Single +import timber.log.Timber +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SemesterRepository @Inject constructor( + private val remote: SemesterRemote, + private val local: SemesterLocal, + private val settings: InternetObservingSettings, + private val apiHelper: ApiHelper +) { + + fun getSemesters(student: Student, forceRefresh: Boolean = false): Single> { + return Maybe.just(apiHelper.initApi(student)) + .flatMap { local.getSemesters(student).filter { !forceRefresh } } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getSemesters(student) else Single.error(UnknownHostException()) + }.flatMap { new -> + val currentSemesters = new.filter { it.isCurrent } + if (currentSemesters.size == 1) { + local.getSemesters(student).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteSemesters(old.uniqueSubtract(new)) + local.saveSemesters(new.uniqueSubtract(old)) + } + } else { + Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}") + throw IllegalArgumentException("Current semester can be only one.") + } + }.flatMap { local.getSemesters(student).toSingle(emptyList()) }) + } + + fun getCurrentSemester(student: Student, forceRefresh: Boolean = false): Single { + return getSemesters(student, forceRefresh).map { item -> item.single { it.isCurrent } } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt new file mode 100644 index 00000000..e6d74421 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt @@ -0,0 +1,46 @@ +package io.github.wulkanowy.data.repositories.student + +import android.content.Context +import io.github.wulkanowy.data.db.dao.StudentDao +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.utils.security.decrypt +import io.github.wulkanowy.utils.security.encrypt +import io.reactivex.Completable +import io.reactivex.Maybe +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class StudentLocal @Inject constructor( + private val studentDb: StudentDao, + private val context: Context +) { + + fun saveStudents(students: List): Single> { + return Single.fromCallable { studentDb.insertAll(students.map { it.copy(password = encrypt(it.password, context)) }) } + } + + fun getStudents(decryptPass: Boolean): Maybe> { + return studentDb.loadAll() + .map { list -> list.map { it.apply { if (decryptPass) password = decrypt(password) } } } + .filter { !it.isEmpty() } + } + + fun getCurrentStudent(decryptPass: Boolean): Maybe { + return studentDb.loadCurrent().map { it.apply { if (decryptPass) password = decrypt(password) } } + } + + fun setCurrentStudent(student: Student): Completable { + return Completable.fromCallable { + studentDb.run { + resetCurrent() + updateCurrent(student.id) + } + } + } + + fun logoutStudent(student: Student): Completable { + return Completable.fromCallable { studentDb.delete(student) } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt new file mode 100644 index 00000000..251d3834 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.data.repositories.student + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.Student +import io.reactivex.Single +import org.threeten.bp.LocalDateTime.now +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class StudentRemote @Inject constructor(private val api: Api) { + + fun getStudents(email: String, password: String, endpoint: String): Single> { + return api.getStudents().map { students -> + students.map { student -> + Student( + email = email, + password = password, + symbol = student.symbol, + studentId = student.studentId, + studentName = student.studentName, + schoolSymbol = student.schoolSymbol, + schoolName = student.schoolName, + className = student.className, + classId = student.classId, + endpoint = endpoint, + loginType = student.loginType.name, + isCurrent = false, + registrationDate = now() + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt new file mode 100644 index 00000000..5c4a6055 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt @@ -0,0 +1,57 @@ +package io.github.wulkanowy.data.repositories.student + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.ApiHelper +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.exceptions.NoCurrentStudentException +import io.reactivex.Completable +import io.reactivex.Maybe +import io.reactivex.Single +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class StudentRepository @Inject constructor( + private val local: StudentLocal, + private val remote: StudentRemote, + private val settings: InternetObservingSettings, + private val apiHelper: ApiHelper +) { + + fun isStudentSaved(): Single = local.getStudents(false).isEmpty.map { !it } + + fun isCurrentStudentSet(): Single = local.getCurrentStudent(false).isEmpty.map { !it } + + fun getStudents(email: String, password: String, endpoint: String, symbol: String = ""): Single> { + return ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + apiHelper.initApi(email, password, symbol, endpoint) + if (it) remote.getStudents(email, password, endpoint) + else Single.error(UnknownHostException("No internet connection")) + } + } + + fun getSavedStudents(decryptPass: Boolean = true): Single> { + return local.getStudents(decryptPass).toSingle(emptyList()) + } + + fun getCurrentStudent(decryptPass: Boolean = true): Single { + return local.getCurrentStudent(decryptPass) + .switchIfEmpty(Maybe.error(NoCurrentStudentException())) + .toSingle() + } + + fun saveStudents(students: List): Single> { + return local.saveStudents(students) + } + + fun switchStudent(student: Student): Completable { + return local.setCurrentStudent(student) + } + + fun logoutStudent(student: Student): Completable { + return local.logoutStudent(student) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectLocal.kt new file mode 100644 index 00000000..63e33401 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectLocal.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.repositories.subject + +import io.github.wulkanowy.data.db.dao.SubjectDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Subject +import io.reactivex.Maybe +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SubjectLocal @Inject constructor(private val subjectDao: SubjectDao) { + + fun getSubjects(semester: Semester): Maybe> { + return subjectDao.loadAll(semester.diaryId, semester.studentId) + .filter { !it.isEmpty() } + } + + fun saveSubjects(subjects: List) { + subjectDao.insertAll(subjects) + } + + fun deleteSubjects(subjects: List) { + subjectDao.deleteAll(subjects) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt new file mode 100644 index 00000000..88fbb196 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt @@ -0,0 +1,27 @@ +package io.github.wulkanowy.data.repositories.subject + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Subject +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SubjectRemote @Inject constructor(private val api: Api) { + + fun getSubjects(semester: Semester): Single> { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { api.getSubjects() } + .map { subjects -> + subjects.map { + Subject( + studentId = semester.studentId, + diaryId = semester.diaryId, + name = it.name, + realId = it.value + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt new file mode 100644 index 00000000..0c5f386b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.data.repositories.subject + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Subject +import io.github.wulkanowy.utils.uniqueSubtract +import io.reactivex.Single +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SubjectRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: SubjectLocal, + private val remote: SubjectRemote +) { + + fun getSubjects(semester: Semester, forceRefresh: Boolean = false): Single> { + return local.getSubjects(semester).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getSubjects(semester) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getSubjects(semester) + .toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteSubjects(old.uniqueSubtract(new)) + local.saveSubjects(new.uniqueSubtract(old)) + } + }.flatMap { + local.getSubjects(semester).toSingle(emptyList()) + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocal.kt new file mode 100644 index 00000000..e074ce2a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocal.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.repositories.timetable + +import io.github.wulkanowy.data.db.dao.TimetableDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Timetable +import io.reactivex.Maybe +import org.threeten.bp.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao) { + + fun saveTimetable(timetables: List) { + timetableDb.insertAll(timetables) + } + + fun deleteTimetable(timetables: List) { + timetableDb.deleteAll(timetables) + } + + fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { + return timetableDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate).filter { it.isNotEmpty() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt new file mode 100644 index 00000000..77742e7b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.data.repositories.timetable + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.utils.toLocalDate +import io.github.wulkanowy.utils.toLocalDateTime +import io.reactivex.Single +import org.threeten.bp.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class TimetableRemote @Inject constructor(private val api: Api) { + + fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { + return Single.just(api.apply { diaryId = semester.diaryId }) + .flatMap { it.getTimetable(startDate, endDate) } + .map { lessons -> + lessons.map { + Timetable( + studentId = semester.studentId, + diaryId = semester.diaryId, + number = it.number, + start = it.start.toLocalDateTime(), + end = it.end.toLocalDateTime(), + date = it.date.toLocalDate(), + subject = it.subject, + subjectOld = it.subjectOld, + group = it.group, + room = it.room, + roomOld = it.roomOld, + teacher = it.teacher, + teacherOld = it.teacherOld, + info = it.info, + changes = it.changes, + canceled = it.canceled + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt new file mode 100644 index 00000000..0e6331f9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt @@ -0,0 +1,49 @@ +package io.github.wulkanowy.data.repositories.timetable + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.utils.friday +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.uniqueSubtract +import io.reactivex.Single +import org.threeten.bp.LocalDate +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class TimetableRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: TimetableLocal, + private val remote: TimetableRemote +) { + + fun getTimetable(semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single> { + return Single.fromCallable { start.monday to end.friday }.flatMap { (monday, friday) -> + local.getTimetable(semester, monday, friday).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap { + if (it) remote.getTimetable(semester, monday, friday) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getTimetable(semester, monday, friday) + .toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteTimetable(old.uniqueSubtract(new)) + local.saveTimetable(new.uniqueSubtract(old).map { item -> + item.apply { + old.singleOrNull { this.start == it.start }?.let { + return@map copy( + room = if (room.isEmpty()) it.room else room + ) + } + } + }) + } + }.flatMap { + local.getTimetable(semester, monday, friday).toSingle(emptyList()) + }).map { list -> list.filter { it.date in start..end } } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/AccountSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/AccountSync.java deleted file mode 100644 index 45f8c1f5..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/AccountSync.java +++ /dev/null @@ -1,197 +0,0 @@ -package io.github.wulkanowy.data.sync; - -import android.content.Context; - -import org.greenrobot.greendao.database.Database; - -import java.io.IOException; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.data.db.dao.entities.Account; -import io.github.wulkanowy.data.db.dao.entities.DaoMaster; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.dao.entities.Diary; -import io.github.wulkanowy.data.db.dao.entities.DiaryDao; -import io.github.wulkanowy.data.db.dao.entities.School; -import io.github.wulkanowy.data.db.dao.entities.SchoolDao; -import io.github.wulkanowy.data.db.dao.entities.Semester; -import io.github.wulkanowy.data.db.dao.entities.Student; -import io.github.wulkanowy.data.db.dao.entities.StudentDao; -import io.github.wulkanowy.data.db.dao.entities.Symbol; -import io.github.wulkanowy.data.db.dao.entities.SymbolDao; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.utils.DataObjectConverter; -import io.github.wulkanowy.utils.security.CryptoException; -import io.github.wulkanowy.utils.security.Scrambler; -import timber.log.Timber; - -@Singleton -public class AccountSync { - - private final DaoSession daoSession; - - private final SharedPrefContract sharedPref; - - private final Vulcan vulcan; - - private final Context context; - - @Inject - AccountSync(DaoSession daoSession, SharedPrefContract sharedPref, - Vulcan vulcan, Context context) { - this.daoSession = daoSession; - this.sharedPref = sharedPref; - this.vulcan = vulcan; - this.context = context; - } - - public void registerUser(String email, String password, String symbol) - throws VulcanException, IOException, CryptoException { - - clearUserData(); - - vulcan.setCredentials(email, password, symbol, null, null, null); - - daoSession.getDatabase().beginTransaction(); - - Timber.i("Register start"); - - try { - Account account = insertAccount(email, password); - Symbol symbolEntity = insertSymbol(account); - School schoolEntity = insertSchools(symbolEntity); - Student student = insertStudents(schoolEntity); - Diary diary = insertDiaries(student); - insertSemesters(diary); - - sharedPref.setCurrentUserId(account.getId()); - - daoSession.getDatabase().setTransactionSuccessful(); - } finally { - daoSession.getDatabase().endTransaction(); - } - - Timber.i("Register end"); - } - - private Account insertAccount(String email, String password) throws CryptoException { - Timber.d("Register account"); - Account account = new Account() - .setEmail(email) - .setPassword(Scrambler.encrypt(email, password, context)); - daoSession.getAccountDao().insert(account); - return account; - } - - private Symbol insertSymbol(Account account) throws VulcanException, IOException { - vulcan.getSchools(); - Timber.d("Register symbol (%s)", vulcan.getSymbol()); - Symbol symbol = new Symbol() - .setUserId(account.getId()) - .setSymbol(vulcan.getSymbol()); - daoSession.getSymbolDao().insert(symbol); - - return symbol; - } - - private School insertSchools(Symbol symbol) throws VulcanException, IOException { - List schoolList = DataObjectConverter.schoolsToSchoolsEntities( - vulcan.getSchools(), - symbol.getId() - ); - Timber.d("Register schools (%s)", schoolList.size()); - daoSession.getSchoolDao().insertInTx(schoolList); - - return daoSession.getSchoolDao().queryBuilder().where( - SchoolDao.Properties.SymbolId.eq(symbol.getId()), - SchoolDao.Properties.Current.eq(true) - ).unique(); - } - - private Student insertStudents(School school) throws VulcanException, IOException { - List studentList = DataObjectConverter.studentsToStudentEntities( - vulcan.getStudentAndParent().getStudents(), - school.getId() - ); - Timber.d("Register students (%s)", studentList.size()); - daoSession.getStudentDao().insertInTx(studentList); - - return daoSession.getStudentDao().queryBuilder().where( - StudentDao.Properties.SchoolId.eq(school.getId()), - StudentDao.Properties.Current.eq(true) - ).unique(); - } - - private Diary insertDiaries(Student student) throws VulcanException, IOException { - List diaryList = DataObjectConverter.diariesToDiaryEntities( - vulcan.getStudentAndParent().getDiaries(), - student.getId() - ); - Timber.d("Register diaries (%s)", diaryList.size()); - daoSession.getDiaryDao().insertInTx(diaryList); - - return daoSession.getDiaryDao().queryBuilder().where( - DiaryDao.Properties.StudentId.eq(student.getId()), - DiaryDao.Properties.Current.eq(true) - ).unique(); - } - - private void insertSemesters(Diary diary) throws VulcanException, IOException { - List semesterList = DataObjectConverter.semestersToSemesterEntities( - vulcan.getStudentAndParent().getSemesters(), - diary.getId() - ); - Timber.d("Register semesters (%s)", semesterList.size()); - daoSession.getSemesterDao().insertInTx(semesterList); - } - - public void initLastUser() throws CryptoException { - - long userId = sharedPref.getCurrentUserId(); - - if (userId == 0) { - throw new NotRegisteredUserException("Can't find user id in SharedPreferences"); - } - - Timber.d("Init current user (%s)", userId); - - Account account = daoSession.getAccountDao().load(userId); - - Symbol symbol = daoSession.getSymbolDao().queryBuilder().where( - SymbolDao.Properties.UserId.eq(account.getId())).unique(); - - School school = daoSession.getSchoolDao().queryBuilder().where( - SchoolDao.Properties.SymbolId.eq(symbol.getId())).unique(); - - Student student = daoSession.getStudentDao().queryBuilder().where( - StudentDao.Properties.SchoolId.eq(school.getId()), - StudentDao.Properties.Current.eq(true) - ).unique(); - - Diary diary = daoSession.getDiaryDao().queryBuilder().where( - DiaryDao.Properties.StudentId.eq(student.getId()), - DiaryDao.Properties.Current.eq(true) - ).unique(); - - vulcan.setCredentials( - account.getEmail(), - Scrambler.decrypt(account.getEmail(), account.getPassword()), - symbol.getSymbol(), - school.getRealId(), - student.getRealId(), - diary.getValue() - ); - } - - private void clearUserData() { - Database database = daoSession.getDatabase(); - DaoMaster.dropAllTables(database, true); - DaoMaster.createAllTables(database, true); - sharedPref.setCurrentUserId(0); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/AttendanceSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/AttendanceSync.java deleted file mode 100644 index 18769418..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/AttendanceSync.java +++ /dev/null @@ -1,140 +0,0 @@ -package io.github.wulkanowy.data.sync; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.api.generic.Lesson; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLessonDao; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.dao.entities.Day; -import io.github.wulkanowy.data.db.dao.entities.DayDao; -import io.github.wulkanowy.data.db.dao.entities.Week; -import io.github.wulkanowy.data.db.dao.entities.WeekDao; -import io.github.wulkanowy.utils.DataObjectConverter; -import timber.log.Timber; - -@Singleton -public class AttendanceSync { - - private final DaoSession daoSession; - - private final Vulcan vulcan; - - private long diaryId; - - @Inject - AttendanceSync(DaoSession daoSession, Vulcan vulcan) { - this.daoSession = daoSession; - this.vulcan = vulcan; - } - - public void syncAttendance(long diaryId, String date) throws IOException, VulcanException { - this.diaryId = diaryId; - - io.github.wulkanowy.api.generic.Week weekApi = getWeekFromApi(date); - Week weekDb = getWeekFromDb(weekApi.getStartDayDate()); - - long weekId = updateWeekInDb(weekDb, weekApi); - - List lessonList = updateDays(weekApi.getDays(), weekId); - - daoSession.getAttendanceLessonDao().saveInTx(lessonList); - - Timber.d("Attendance synchronization complete (%s)", lessonList.size()); - } - - private io.github.wulkanowy.api.generic.Week getWeekFromApi(String date) - throws IOException, VulcanException { - return vulcan.getAttendanceTable().getWeekTable(date); - } - - private Week getWeekFromDb(String date) { - return daoSession.getWeekDao().queryBuilder().where( - WeekDao.Properties.DiaryId.eq(diaryId), - WeekDao.Properties.StartDayDate.eq(date) - ).unique(); - } - - private Long updateWeekInDb(Week dbWeekEntity, io.github.wulkanowy.api.generic.Week fromApi) { - if (dbWeekEntity != null) { - dbWeekEntity.setAttendanceSynced(true); - dbWeekEntity.update(); - - return dbWeekEntity.getId(); - } - - Week apiWeekEntity = DataObjectConverter.weekToWeekEntity(fromApi).setDiaryId(diaryId); - apiWeekEntity.setAttendanceSynced(true); - - return daoSession.getWeekDao().insert(apiWeekEntity); - } - - private List updateDays(List dayListFromApi, long weekId) { - List updatedLessonList = new ArrayList<>(); - - for (io.github.wulkanowy.api.generic.Day dayFromApi : dayListFromApi) { - - Day dbDayEntity = getDayFromDb(dayFromApi.getDate(), weekId); - - Day apiDayEntity = DataObjectConverter.dayToDayEntity(dayFromApi); - - long dayId = updateDay(dbDayEntity, apiDayEntity, weekId); - - updateLessons(dayFromApi.getLessons(), updatedLessonList, dayId); - } - - return updatedLessonList; - } - - private Day getDayFromDb(String date, long weekId) { - return daoSession.getDayDao().queryBuilder() - .where( - DayDao.Properties.WeekId.eq(weekId), - DayDao.Properties.Date.eq(date) - ).unique(); - } - - private long updateDay(Day dbDayEntity, Day apiDayEntity, long weekId) { - if (null != dbDayEntity) { - return dbDayEntity.getId(); - } - - apiDayEntity.setWeekId(weekId); - - return daoSession.getDayDao().insert(apiDayEntity); - } - - private void updateLessons(List lessons, List updatedLessons, long dayId) { - List lessonsFromApiEntities = DataObjectConverter - .lessonsToAttendanceLessonsEntities(lessons); - - for (AttendanceLesson apiLessonEntity : lessonsFromApiEntities) { - AttendanceLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId); - - apiLessonEntity.setDayId(dayId); - - if (lessonFromDb != null) { - apiLessonEntity.setId(lessonFromDb.getId()); - } - - if (!"".equals(apiLessonEntity.getSubject())) { - updatedLessons.add(apiLessonEntity); - } - } - } - - private AttendanceLesson getLessonFromDb(AttendanceLesson apiEntity, long dayId) { - return daoSession.getAttendanceLessonDao().queryBuilder() - .where(AttendanceLessonDao.Properties.DayId.eq(dayId), - AttendanceLessonDao.Properties.Date.eq(apiEntity.getDate()), - AttendanceLessonDao.Properties.Number.eq(apiEntity.getNumber())) - .unique(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/ExamsSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/ExamsSync.java deleted file mode 100644 index db473092..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/ExamsSync.java +++ /dev/null @@ -1,135 +0,0 @@ -package io.github.wulkanowy.data.sync; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.api.exams.ExamDay; -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.Exam; -import io.github.wulkanowy.data.db.dao.entities.ExamDao; -import io.github.wulkanowy.data.db.dao.entities.Week; -import io.github.wulkanowy.data.db.dao.entities.WeekDao; -import io.github.wulkanowy.utils.DataObjectConverter; -import timber.log.Timber; - -public class ExamsSync { - - private final DaoSession daoSession; - - private final Vulcan vulcan; - - private long diaryId; - - @Inject - ExamsSync(DaoSession daoSession, Vulcan vulcan) { - this.daoSession = daoSession; - this.vulcan = vulcan; - } - - public void syncExams(long diaryId, String date) throws IOException, VulcanException { - this.diaryId = diaryId; - - io.github.wulkanowy.api.generic.Week weekApi = getWeekFromApi(date); - Week weekDb = getWeekFromDb(weekApi.getStartDayDate()); - - long weekId = updateWeekInDb(weekDb, weekApi); - - List examList = getPreparedExams(weekApi.getDays(), weekId); - - daoSession.getExamDao().saveInTx(examList); - - Timber.d("Exams synchronization complete (%s)", examList.size()); - } - - private Week getWeekFromDb(String date) { - return daoSession.getWeekDao().queryBuilder().where( - WeekDao.Properties.DiaryId.eq(diaryId), - WeekDao.Properties.StartDayDate.eq(date) - ).unique(); - } - - private io.github.wulkanowy.api.generic.Week getWeekFromApi(String date) - throws VulcanException, IOException { - return vulcan.getExamsList().getWeek(date, true); - } - - private Long updateWeekInDb(Week weekDb, io.github.wulkanowy.api.generic.Week weekApi) { - if (weekDb != null) { - weekDb.setExamsSynced(true); - weekDb.update(); - - return weekDb.getId(); - } - - Week weekApiEntity = DataObjectConverter.weekToWeekEntity(weekApi).setDiaryId(diaryId); - weekApiEntity.setExamsSynced(true); - - return daoSession.getWeekDao().insert(weekApiEntity); - } - - private Day getDayFromDb(String date, long weekId) { - return daoSession.getDayDao().queryBuilder().where( - DayDao.Properties.WeekId.eq(weekId), - DayDao.Properties.Date.eq(date) - ).unique(); - } - - private List getPreparedExams(List dayListFromApi, - long weekId) { - List preparedExamList = new ArrayList<>(); - - for (ExamDay dayFromApi : dayListFromApi) { - - Day dayDb = getDayFromDb(dayFromApi.getDate(), weekId); - - Day dayApiEntity = DataObjectConverter.dayToDayEntity(dayFromApi); - - long dayId = updateDayInDb(dayDb, dayApiEntity, weekId); - - prepareExam(dayFromApi.getExamList(), preparedExamList, dayId); - } - return preparedExamList; - } - - private long updateDayInDb(Day dayDb, Day dayApi, long weekId) { - dayApi.setWeekId(weekId); - - if (null != dayDb) { - return dayDb.getId(); - } - return daoSession.getDayDao().insert(dayApi); - } - - private void prepareExam(List examList, - List preparedExams, long dayId) { - List examsApiEntity = DataObjectConverter.examsToExamsEntity(examList); - - for (Exam examApi : examsApiEntity) { - Exam examDb = getExamFromDb(examApi, dayId); - - examApi.setDayId(dayId); - - if (examDb != null) { - examApi.setId(examDb.getId()); - } - preparedExams.add(examApi); - } - } - - private Exam getExamFromDb(Exam examApi, long dayId) { - return daoSession.getExamDao().queryBuilder() - .where(ExamDao.Properties.DayId.eq(dayId), - ExamDao.Properties.EntryDate.eq(examApi.getEntryDate()), - ExamDao.Properties.SubjectAndGroup.eq(examApi.getSubjectAndGroup()), - ExamDao.Properties.Type.eq(examApi.getType()), - ExamDao.Properties.Teacher.eq(examApi.getTeacher())) - .unique(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/GradeSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/GradeSync.java deleted file mode 100644 index 59a71845..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/GradeSync.java +++ /dev/null @@ -1,81 +0,0 @@ -package io.github.wulkanowy.data.sync; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.dao.entities.Grade; -import io.github.wulkanowy.data.db.dao.entities.Semester; -import io.github.wulkanowy.data.db.dao.entities.SubjectDao; -import io.github.wulkanowy.utils.DataObjectConverter; -import io.github.wulkanowy.utils.EntitiesCompare; -import timber.log.Timber; - -@Singleton -public class GradeSync { - - private final DaoSession daoSession; - - private final Vulcan vulcan; - - private long semesterId; - - @Inject - GradeSync(DaoSession daoSession, Vulcan vulcan) { - this.daoSession = daoSession; - this.vulcan = vulcan; - } - - public void sync(long semesterId) throws IOException, VulcanException { - this.semesterId = semesterId; - - Semester semester = daoSession.getSemesterDao().load(semesterId); - resetSemesterRelations(semester); - - List lastList = getUpdatedList(getComparedList(semester)); - - daoSession.getGradeDao().deleteInTx(semester.getGradeList()); - daoSession.getGradeDao().insertInTx(lastList); - - Timber.d("Grades synchronization complete (%s)", lastList.size()); - } - - private void resetSemesterRelations(Semester semester) { - semester.resetSubjectList(); - semester.resetGradeList(); - } - - private List getUpdatedList(List comparedList) { - List updatedList = new ArrayList<>(); - - for (Grade grade : comparedList) { - grade.setSemesterId(semesterId); - grade.setSubjectId(getSubjectId(grade.getSubject())); - updatedList.add(grade); - } - - return updatedList; - } - - private List getComparedList(Semester semester) throws IOException, VulcanException { - List gradesFromNet = DataObjectConverter.gradesToGradeEntities( - vulcan.getGradesList().getAll(semester.getValue()), semesterId); - - List gradesFromDb = semester.getGradeList(); - - return EntitiesCompare.compareGradeList(gradesFromNet, gradesFromDb); - } - - private Long getSubjectId(String subjectName) { - return daoSession.getSubjectDao().queryBuilder().where( - SubjectDao.Properties.Name.eq(subjectName), - SubjectDao.Properties.SemesterId.eq(semesterId) - ).build().uniqueOrThrow().getId(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/NotRegisteredUserException.java b/app/src/main/java/io/github/wulkanowy/data/sync/NotRegisteredUserException.java deleted file mode 100644 index ddba1a21..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/NotRegisteredUserException.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.wulkanowy.data.sync; - -public class NotRegisteredUserException extends RuntimeException { - - public NotRegisteredUserException(String message) { - super(message); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/SubjectSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/SubjectSync.java deleted file mode 100644 index 1d0300d6..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/SubjectSync.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.github.wulkanowy.data.sync; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.dao.entities.Semester; -import io.github.wulkanowy.data.db.dao.entities.Subject; -import io.github.wulkanowy.utils.DataObjectConverter; -import timber.log.Timber; - -@Singleton -public class SubjectSync { - - private final DaoSession daoSession; - - private final Vulcan vulcan; - - private long semesterId; - - @Inject - SubjectSync(DaoSession daoSession, Vulcan vulcan) { - this.daoSession = daoSession; - this.vulcan = vulcan; - } - - public void sync(long semesterId) throws VulcanException, IOException { - this.semesterId = semesterId; - - Semester semester = daoSession.getSemesterDao().load(semesterId); - - List lastList = getUpdatedList(getSubjectsFromNet(semester)); - - daoSession.getSubjectDao().deleteInTx(getSubjectsFromDb()); - daoSession.getSubjectDao().insertInTx(lastList); - - Timber.d("Subjects synchronization complete (%s)", lastList.size()); - } - - private List getSubjectsFromNet(Semester semester) throws VulcanException, IOException { - return DataObjectConverter.subjectsToSubjectEntities( - vulcan.getSubjectsList().getAll(semester.getValue()), semesterId); - } - - private List getSubjectsFromDb() { - Semester semester = daoSession.getSemesterDao().load(semesterId); - semester.resetSubjectList(); - return semester.getSubjectList(); - } - - private List getUpdatedList(List subjectsFromNet) { - List updatedList = new ArrayList<>(); - - for (Subject subject : subjectsFromNet) { - subject.setSemesterId(semesterId); - updatedList.add(subject); - } - return updatedList; - } -} 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 deleted file mode 100644 index 67797ad1..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/SyncContract.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.data.sync; - -import java.io.IOException; -import java.text.ParseException; - -import javax.inject.Singleton; - -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.utils.security.CryptoException; - -@Singleton -public interface SyncContract { - - void registerUser(String email, String password, String symbol) throws VulcanException, - IOException, CryptoException; - - void initLastUser() throws IOException, CryptoException; - - void syncGrades(int semesterName) throws VulcanException, IOException, ParseException; - - void syncGrades() throws VulcanException, IOException, ParseException; - - void syncSubjects(int semesterName) throws VulcanException, IOException; - - void syncSubjects() throws VulcanException, IOException; - - void syncAttendance() throws ParseException, IOException, VulcanException; - - void syncAttendance(long diaryId, String date) throws ParseException, IOException, VulcanException; - - void syncTimetable() throws VulcanException, IOException, ParseException; - - void syncTimetable(long diaryId, String date) throws VulcanException, IOException, ParseException; - - void syncExams() throws VulcanException, IOException, ParseException; - - void syncExams(long diaryId, String date) throws VulcanException, IOException, ParseException; - - void syncAll() throws VulcanException, IOException, ParseException; -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/SyncRepository.java b/app/src/main/java/io/github/wulkanowy/data/sync/SyncRepository.java deleted file mode 100644 index 05dd14fd..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/SyncRepository.java +++ /dev/null @@ -1,123 +0,0 @@ -package io.github.wulkanowy.data.sync; - -import java.io.IOException; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.data.db.dao.DbContract; -import io.github.wulkanowy.utils.security.CryptoException; - -@Singleton -public class SyncRepository implements SyncContract { - - private final GradeSync gradeSync; - - private final SubjectSync subjectSync; - - private final AttendanceSync attendanceSync; - - private final TimetableSync timetableSync; - - private final AccountSync accountSync; - - private final ExamsSync examsSync; - - private final DbContract database; - - @Inject - SyncRepository(GradeSync gradeSync, SubjectSync subjectSync, AttendanceSync attendanceSync, - TimetableSync timetableSync, AccountSync accountSync, ExamsSync examsSync, - DbContract database) { - this.gradeSync = gradeSync; - this.subjectSync = subjectSync; - this.attendanceSync = attendanceSync; - this.timetableSync = timetableSync; - this.accountSync = accountSync; - this.examsSync = examsSync; - this.database = database; - } - - @Override - public void registerUser(String email, String password, String symbol) throws VulcanException, - IOException, CryptoException { - accountSync.registerUser(email, password, symbol); - } - - @Override - public void initLastUser() throws CryptoException { - accountSync.initLastUser(); - } - - @Override - public void syncGrades(int semesterName) throws VulcanException, IOException { - gradeSync.sync(semesterName); - } - - @Override - public void syncGrades() throws VulcanException, IOException { - gradeSync.sync(database.getCurrentSemesterId()); - } - - @Override - public void syncSubjects(int semesterName) throws VulcanException, IOException { - subjectSync.sync(semesterName); - } - - @Override - public void syncSubjects() throws VulcanException, IOException { - subjectSync.sync(database.getCurrentSemesterId()); - } - - @Override - public void syncAttendance() throws IOException, VulcanException { - attendanceSync.syncAttendance(database.getCurrentDiaryId(), null); - } - - @Override - public void syncAttendance(long diaryId, String date) throws IOException, VulcanException { - if (diaryId != 0) { - attendanceSync.syncAttendance(diaryId, date); - } else { - attendanceSync.syncAttendance(database.getCurrentDiaryId(), date); - } - } - - @Override - public void syncTimetable() throws VulcanException, IOException { - timetableSync.syncTimetable(database.getCurrentDiaryId(), null); - } - - @Override - public void syncTimetable(long diaryId, String date) throws VulcanException, IOException { - if (diaryId != 0) { - timetableSync.syncTimetable(diaryId, date); - } else { - timetableSync.syncTimetable(database.getCurrentDiaryId(), date); - } - } - - @Override - public void syncExams() throws VulcanException, IOException { - examsSync.syncExams(database.getCurrentDiaryId(), null); - } - - @Override - public void syncExams(long diaryId, String date) throws VulcanException, IOException { - if (diaryId != 0) { - examsSync.syncExams(diaryId, date); - } else { - examsSync.syncExams(database.getCurrentDiaryId(), date); - } - } - - @Override - public void syncAll() throws VulcanException, IOException { - syncSubjects(); - syncGrades(); - syncAttendance(); - syncTimetable(); - syncExams(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/sync/TimetableSync.java b/app/src/main/java/io/github/wulkanowy/data/sync/TimetableSync.java deleted file mode 100644 index 1ea2fa48..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/sync/TimetableSync.java +++ /dev/null @@ -1,161 +0,0 @@ -package io.github.wulkanowy.data.sync; - -import org.apache.commons.collections4.CollectionUtils; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.api.VulcanException; -import io.github.wulkanowy.api.generic.Lesson; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.dao.entities.Day; -import io.github.wulkanowy.data.db.dao.entities.DayDao; -import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; -import io.github.wulkanowy.data.db.dao.entities.TimetableLessonDao; -import io.github.wulkanowy.data.db.dao.entities.Week; -import io.github.wulkanowy.data.db.dao.entities.WeekDao; -import io.github.wulkanowy.utils.DataObjectConverter; -import timber.log.Timber; - -@Singleton -public class TimetableSync { - - private final DaoSession daoSession; - - private final Vulcan vulcan; - - private long diaryId; - - @Inject - TimetableSync(DaoSession daoSession, Vulcan vulcan) { - this.daoSession = daoSession; - this.vulcan = vulcan; - } - - public void syncTimetable(long diaryId, String date) throws IOException, VulcanException { - this.diaryId = diaryId; - - io.github.wulkanowy.api.generic.Week weekApi = getWeekFromApi(date); - Week weekDb = getWeekFromDb(weekApi.getStartDayDate()); - - long weekId = updateWeekInDb(weekDb, weekApi); - - List lessonList = updateDays(weekApi.getDays(), weekId); - - daoSession.getTimetableLessonDao().saveInTx(lessonList); - - Timber.d("Timetable synchronization complete (%s)", lessonList.size()); - } - - private io.github.wulkanowy.api.generic.Week getWeekFromApi(String date) - throws IOException, VulcanException { - return vulcan.getTimetable().getWeekTable(date); - } - - private Week getWeekFromDb(String date) { - return daoSession.getWeekDao().queryBuilder().where( - WeekDao.Properties.DiaryId.eq(diaryId), - WeekDao.Properties.StartDayDate.eq(date) - ).unique(); - } - - private Long updateWeekInDb(Week dbEntity, io.github.wulkanowy.api.generic.Week fromApi) { - if (dbEntity != null) { - dbEntity.setTimetableSynced(true); - dbEntity.update(); - - return dbEntity.getId(); - } - - Week apiEntity = DataObjectConverter.weekToWeekEntity(fromApi).setDiaryId(diaryId); - apiEntity.setTimetableSynced(true); - - return daoSession.getWeekDao().insert(apiEntity); - } - - private List updateDays(List dayListFromApi, long weekId) { - List updatedLessonList = new ArrayList<>(); - - for (io.github.wulkanowy.api.timetable.TimetableDay dayFromApi : dayListFromApi) { - - Day dbDayEntity = getDayFromDb(dayFromApi.getDate(), weekId); - - Day apiDayEntity = DataObjectConverter.timetableDayToDayEntity(dayFromApi); - - long dayId = updateDay(dbDayEntity, apiDayEntity, weekId); - - updateLessons(dayFromApi.getLessons(), updatedLessonList, dayId); - } - - return updatedLessonList; - } - - private Day getDayFromDb(String date, long weekId) { - return daoSession.getDayDao().queryBuilder().where( - DayDao.Properties.WeekId.eq(weekId), - DayDao.Properties.Date.eq(date) - ).unique(); - } - - private long updateDay(Day dayFromDb, Day apiDayEntity, long weekId) { - apiDayEntity.setWeekId(weekId); - - if (null != dayFromDb) { - apiDayEntity.setId(dayFromDb.getId()); - - daoSession.getDayDao().save(apiDayEntity); - dayFromDb.refresh(); - - return dayFromDb.getId(); - } - - return daoSession.getDayDao().insert(apiDayEntity); - } - - private void updateLessons(List lessons, List updatedLessons, long dayId) { - List lessonsFromApiEntities = DataObjectConverter - .lessonsToTimetableLessonsEntities(lessons); - - List lessonsFromDbEntities = getLessonsFromDb(dayId); - - if (!lessonsFromDbEntities.isEmpty()) { - List lessonToRemove = new ArrayList<>(CollectionUtils.removeAll(lessonsFromDbEntities, lessonsFromApiEntities)); - - for (TimetableLesson timetableLesson : lessonToRemove) { - daoSession.getTimetableLessonDao().delete(timetableLesson); - } - } - - for (TimetableLesson apiLessonEntity : lessonsFromApiEntities) { - TimetableLesson lessonFromDb = getLessonFromDb(apiLessonEntity, dayId); - - apiLessonEntity.setDayId(dayId); - - if (lessonFromDb != null) { - apiLessonEntity.setId(lessonFromDb.getId()); - } - - if (!"".equals(apiLessonEntity.getSubject())) { - updatedLessons.add(apiLessonEntity); - } - } - } - - private TimetableLesson getLessonFromDb(TimetableLesson apiEntity, long dayId) { - return daoSession.getTimetableLessonDao().queryBuilder() - .where(TimetableLessonDao.Properties.DayId.eq(dayId), - TimetableLessonDao.Properties.Date.eq(apiEntity.getDate()), - TimetableLessonDao.Properties.StartTime.eq(apiEntity.getStartTime()), - TimetableLessonDao.Properties.EndTime.eq(apiEntity.getEndTime())) - .unique(); - } - - private List getLessonsFromDb(long dayId) { - return daoSession.getDayDao().load(dayId).getTimetableLessons(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/di/AppComponent.java b/app/src/main/java/io/github/wulkanowy/di/AppComponent.java deleted file mode 100644 index 92f5d9a1..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/AppComponent.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.di; - -import javax.inject.Singleton; - -import dagger.Component; -import dagger.android.AndroidInjector; -import dagger.android.support.AndroidSupportInjectionModule; -import io.github.wulkanowy.WulkanowyApp; - -@Singleton -@Component(modules = { - AndroidSupportInjectionModule.class, - AppModule.class, - BuilderModule.class -}) -public interface AppComponent extends AndroidInjector { - @Component.Builder - abstract class Builder extends AndroidInjector.Builder { - } -} diff --git a/app/src/main/java/io/github/wulkanowy/di/AppComponent.kt b/app/src/main/java/io/github/wulkanowy/di/AppComponent.kt new file mode 100644 index 00000000..3ac3a525 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/AppComponent.kt @@ -0,0 +1,22 @@ +package io.github.wulkanowy.di + +import dagger.Component +import dagger.android.AndroidInjector +import dagger.android.support.AndroidSupportInjectionModule +import io.github.wulkanowy.WulkanowyApp +import io.github.wulkanowy.data.RepositoryModule +import io.github.wulkanowy.services.ServicesModule +import javax.inject.Singleton + +@Singleton +@Component(modules = [ + AndroidSupportInjectionModule::class, + AppModule::class, + RepositoryModule::class, + ServicesModule::class, + BuilderModule::class]) +interface AppComponent : AndroidInjector { + + @Component.Factory + interface Factory : AndroidInjector.Factory +} diff --git a/app/src/main/java/io/github/wulkanowy/di/AppModule.java b/app/src/main/java/io/github/wulkanowy/di/AppModule.java deleted file mode 100644 index d6d4dfa2..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/AppModule.java +++ /dev/null @@ -1,78 +0,0 @@ -package io.github.wulkanowy.di; - -import android.content.Context; - -import javax.inject.Named; -import javax.inject.Singleton; - -import dagger.Binds; -import dagger.Module; -import dagger.Provides; -import io.github.wulkanowy.WulkanowyApp; -import io.github.wulkanowy.api.Vulcan; -import io.github.wulkanowy.data.Repository; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.data.db.dao.DbContract; -import io.github.wulkanowy.data.db.dao.DbHelper; -import io.github.wulkanowy.data.db.dao.DbRepository; -import io.github.wulkanowy.data.db.dao.entities.DaoMaster; -import io.github.wulkanowy.data.db.dao.entities.DaoSession; -import io.github.wulkanowy.data.db.resources.ResourcesContract; -import io.github.wulkanowy.data.db.resources.ResourcesRepository; -import io.github.wulkanowy.data.db.shared.SharedPrefContract; -import io.github.wulkanowy.data.db.shared.SharedPrefRepository; -import io.github.wulkanowy.data.sync.SyncContract; -import io.github.wulkanowy.data.sync.SyncRepository; -import io.github.wulkanowy.utils.AppConstant; - -@Module -public abstract class AppModule { - - @Binds - abstract Context provideContext(WulkanowyApp app); - - @Singleton - @Binds - abstract RepositoryContract provideRepository(Repository repository); - - @Singleton - @Binds - abstract DbContract provideDatabse(DbRepository dbRepository); - - @Singleton - @Binds - abstract SharedPrefContract provideSharedPref(SharedPrefRepository sharedPrefRepository); - - @Singleton - @Binds - abstract SyncContract provideSync(SyncRepository syncRepository); - - @Singleton - @Binds - abstract ResourcesContract provideResources(ResourcesRepository resourcesRepository); - - @Singleton - @Provides - static DaoSession provideDaoSession(DbHelper dbHelper) { - return new DaoMaster(dbHelper.getWritableDb()).newSession(); - } - - @Singleton - @Provides - static Vulcan provideVulcan() { - return new Vulcan(); - } - - @Provides - @Named("dbName") - static String provideDbName() { - return AppConstant.DATABASE_NAME; - } - - @Provides - @Named("sharedPrefName") - static String provideSharedPrefName() { - return AppConstant.SHARED_PREFERENCES_NAME; - } - -} diff --git a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt new file mode 100644 index 00000000..9ea2bf10 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.di + +import android.appwidget.AppWidgetManager +import android.content.Context +import dagger.Module +import dagger.Provides +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.BuildConfig.DEBUG +import io.github.wulkanowy.WulkanowyApp +import io.github.wulkanowy.utils.SchedulersProvider +import javax.inject.Named +import javax.inject.Singleton + +@Module +internal class AppModule { + + @Singleton + @Provides + fun provideContext(app: WulkanowyApp): Context = app + + @Singleton + @Provides + fun provideSchedulersProvider() = SchedulersProvider() + + @Provides + fun provideFlexibleAdapter() = FlexibleAdapter>(null, null, true) + + @Singleton + @Provides + fun provideAppWidgetManager(context: Context): AppWidgetManager = AppWidgetManager.getInstance(context) + + @Singleton + @Named("isDebug") + @Provides + fun provideIsDebug() = DEBUG +} diff --git a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.java b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.java deleted file mode 100644 index 74b77d52..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.java +++ /dev/null @@ -1,39 +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.jobs.SyncJob; -import io.github.wulkanowy.services.widgets.TimetableWidgetServices; -import io.github.wulkanowy.ui.login.LoginActivity; -import io.github.wulkanowy.ui.login.LoginModule; -import io.github.wulkanowy.ui.main.MainActivity; -import io.github.wulkanowy.ui.main.MainModule; -import io.github.wulkanowy.ui.splash.SplashActivity; -import io.github.wulkanowy.ui.splash.SplashModule; -import io.github.wulkanowy.ui.widgets.TimetableWidgetProvider; - -@Module -abstract class BuilderModule { - - @PerActivity - @ContributesAndroidInjector(modules = SplashModule.class) - abstract SplashActivity bindSplashActivity(); - - @PerActivity - @ContributesAndroidInjector(modules = LoginModule.class) - abstract LoginActivity bindLoginActivity(); - - @PerActivity - @ContributesAndroidInjector(modules = MainModule.class) - abstract MainActivity bindMainActivity(); - - @ContributesAndroidInjector - abstract SyncJob bindSyncJob(); - - @ContributesAndroidInjector - abstract TimetableWidgetServices bindTimetableWidgetServices(); - - @ContributesAndroidInjector - abstract TimetableWidgetProvider bindTimetableWidgetProvider(); -} diff --git a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt new file mode 100644 index 00000000..a963e9a9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt @@ -0,0 +1,46 @@ +package io.github.wulkanowy.di + +import dagger.Module +import dagger.android.ContributesAndroidInjector +import io.github.wulkanowy.di.scopes.PerActivity +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.login.LoginModule +import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainModule +import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity +import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider + +@Module +internal abstract class BuilderModule { + + @PerActivity + @ContributesAndroidInjector + abstract fun bindSplashActivity(): SplashActivity + + @PerActivity + @ContributesAndroidInjector(modules = [LoginModule::class]) + abstract fun bindLoginActivity(): LoginActivity + + @PerActivity + @ContributesAndroidInjector(modules = [MainModule::class]) + abstract fun bindMainActivity(): MainActivity + + @ContributesAndroidInjector + abstract fun bindMessageSendActivity(): SendMessageActivity + + @ContributesAndroidInjector + abstract fun bindTimetableWidgetAccountActivity(): TimetableWidgetConfigureActivity + + @ContributesAndroidInjector + abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider + + @ContributesAndroidInjector + abstract fun bindLuckyNumberWidgetAccountActivity(): LuckyNumberWidgetConfigureActivity + + @ContributesAndroidInjector + abstract fun bindLuckyNumberWidgetProvider(): LuckyNumberWidgetProvider +} diff --git a/app/src/main/java/io/github/wulkanowy/di/scopes/PerActivity.java b/app/src/main/java/io/github/wulkanowy/di/scopes/PerActivity.java deleted file mode 100644 index c9fc8a5b..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/scopes/PerActivity.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.scopes; - -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/scopes/PerActivity.kt b/app/src/main/java/io/github/wulkanowy/di/scopes/PerActivity.kt new file mode 100644 index 00000000..c1b4352a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/scopes/PerActivity.kt @@ -0,0 +1,7 @@ +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.java b/app/src/main/java/io/github/wulkanowy/di/scopes/PerChildFragment.java deleted file mode 100644 index 67a9f820..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/scopes/PerChildFragment.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.scopes; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Scope; - -@Scope -@Retention(RetentionPolicy.RUNTIME) -public @interface PerChildFragment { -} 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 new file mode 100644 index 00000000..08884dac --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/scopes/PerChildFragment.kt @@ -0,0 +1,7 @@ +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.java b/app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.java deleted file mode 100644 index a4d37c38..00000000 --- a/app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.di.scopes; - -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/scopes/PerFragment.kt b/app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.kt new file mode 100644 index 00000000..1203d7d8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.kt @@ -0,0 +1,7 @@ +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/ServicesModule.kt b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt new file mode 100644 index 00000000..84409a84 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt @@ -0,0 +1,107 @@ +package io.github.wulkanowy.services + +import android.app.NotificationManager +import android.content.Context +import android.content.Context.NOTIFICATION_SERVICE +import androidx.core.app.NotificationManagerCompat +import androidx.work.WorkManager +import com.squareup.inject.assisted.dagger2.AssistedModule +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.android.ContributesAndroidInjector +import dagger.multibindings.IntoSet +import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork +import io.github.wulkanowy.services.sync.works.AttendanceWork +import io.github.wulkanowy.services.sync.works.CompletedLessonWork +import io.github.wulkanowy.services.sync.works.ExamWork +import io.github.wulkanowy.services.sync.works.GradeStatisticsWork +import io.github.wulkanowy.services.sync.works.GradeSummaryWork +import io.github.wulkanowy.services.sync.works.GradeWork +import io.github.wulkanowy.services.sync.works.HomeworkWork +import io.github.wulkanowy.services.sync.works.LuckyNumberWork +import io.github.wulkanowy.services.sync.works.MessageWork +import io.github.wulkanowy.services.sync.works.NoteWork +import io.github.wulkanowy.services.sync.works.RecipientWork +import io.github.wulkanowy.services.sync.works.TimetableWork +import io.github.wulkanowy.services.sync.works.Work +import io.github.wulkanowy.services.widgets.TimetableWidgetService +import javax.inject.Singleton + +@AssistedModule +@Module(includes = [AssistedInject_ServicesModule::class]) +abstract class ServicesModule { + + @Module + companion object { + + @JvmStatic + @Provides + fun provideWorkManager() = WorkManager.getInstance() + + @JvmStatic + @Singleton + @Provides + fun provideNotificationManagerCompat(context: Context) = NotificationManagerCompat.from(context) + + @JvmStatic + @Singleton + @Provides + fun provideNotificationManager(context: Context) = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager + } + + @ContributesAndroidInjector + abstract fun bindTimetableWidgetService(): TimetableWidgetService + + @Binds + @IntoSet + abstract fun provideGradeWork(work: GradeWork): Work + + @Binds + @IntoSet + abstract fun provideNoteWork(work: NoteWork): Work + + @Binds + @IntoSet + abstract fun provideAttendanceWork(work: AttendanceWork): Work + + @Binds + @IntoSet + abstract fun provideGradeSummaryWork(work: GradeSummaryWork): Work + + @Binds + @IntoSet + abstract fun provideExamWork(work: ExamWork): Work + + @Binds + @IntoSet + abstract fun provideAttendanceSummaryWork(work: AttendanceSummaryWork): Work + + @Binds + @IntoSet + abstract fun provideTimetableWork(work: TimetableWork): Work + + @Binds + @IntoSet + abstract fun provideLuckyNumberWork(work: LuckyNumberWork): Work + + @Binds + @IntoSet + abstract fun provideCompletedLessonWork(work: CompletedLessonWork): Work + + @Binds + @IntoSet + abstract fun provideHomeworkWork(work: HomeworkWork): Work + + @Binds + @IntoSet + abstract fun provideMessageWork(work: MessageWork): Work + + @Binds + @IntoSet + abstract fun provideRecipientWork(work: RecipientWork): Work + + @Binds + @IntoSet + abstract fun provideGradeStatistics(work: GradeStatisticsWork): Work +} diff --git a/app/src/main/java/io/github/wulkanowy/services/jobs/SyncJob.java b/app/src/main/java/io/github/wulkanowy/services/jobs/SyncJob.java deleted file mode 100644 index 9854666e..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/jobs/SyncJob.java +++ /dev/null @@ -1,148 +0,0 @@ -package io.github.wulkanowy.services.jobs; - -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.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import dagger.android.AndroidInjection; -import io.github.wulkanowy.R; -import io.github.wulkanowy.api.login.BadCredentialsException; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.data.db.dao.entities.Grade; -import io.github.wulkanowy.data.sync.NotRegisteredUserException; -import io.github.wulkanowy.services.notifies.GradeNotify; -import io.github.wulkanowy.ui.main.MainActivity; -import io.github.wulkanowy.utils.FabricUtils; -import timber.log.Timber; - -import static io.github.wulkanowy.utils.TimeUtilsKt.isHolidays; - -public class SyncJob extends SimpleJobService { - - public static final String JOB_TAG = "SyncJob"; - - private List gradeList = new ArrayList<>(); - - @Inject - RepositoryContract repository; - - public static void start(Context context, int interval, boolean useOnlyWifi) { - FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context)); - - dispatcher.mustSchedule(dispatcher.newJobBuilder() - .setLifetime(Lifetime.FOREVER) - .setService(SyncJob.class) - .setTag(JOB_TAG) - .setRecurring(true) - .setTrigger(Trigger.executionWindow(interval * 60, (interval + 10) * 60)) - .setConstraints(useOnlyWifi ? Constraint.ON_UNMETERED_NETWORK : Constraint.ON_ANY_NETWORK) - .setReplaceCurrent(false) - .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) - .build()); - } - - public static void stop(Context context) { - new FirebaseJobDispatcher(new GooglePlayDriver(context)).cancel(JOB_TAG); - } - - @Override - public void onCreate() { - super.onCreate(); - AndroidInjection.inject(this); - } - - @Override - public int onRunJob(JobParameters job) { - if (isHolidays()) { - stop(getApplicationContext()); - - return JobService.RESULT_FAIL_NORETRY; - } - - try { - repository.getSyncRepo().initLastUser(); - repository.getSyncRepo().syncAll(); - - gradeList = repository.getDbRepo().getNewGrades(repository.getDbRepo().getCurrentSemesterName()); - - if (!gradeList.isEmpty() && repository.getSharedRepo().isNotifyEnable()) { - showNotification(); - } - - FabricUtils.logLogin("Background", true); - - return JobService.RESULT_SUCCESS; - } catch (NotRegisteredUserException e) { - logError(e); - stop(getApplicationContext()); - - return JobService.RESULT_FAIL_NORETRY; - } catch (BadCredentialsException e) { - logError(e); - repository.cleanAllData(); - stop(getApplicationContext()); - - return JobService.RESULT_FAIL_NORETRY; - } catch (Exception e) { - logError(e); - - return JobService.RESULT_FAIL_RETRY; - } - } - - private void showNotification() { - GradeNotify gradeNotify = new GradeNotify(getApplicationContext()); - - gradeNotify.notify(gradeNotify.notificationBuilder() - .setContentTitle(getStringTitle()) - .setContentText(getStringContent()) - .setSmallIcon(R.drawable.ic_notify_grade) - .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(MainActivity.EXTRA_CARD_ID_KEY, 0) - , PendingIntent.FLAG_UPDATE_CURRENT - )) - .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()); - } - } - - private void logError(Exception e) { - FabricUtils.logLogin("Background", false); - Timber.e(e, "During background synchronization an error occurred"); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/notifies/GradeNotify.java b/app/src/main/java/io/github/wulkanowy/services/notifies/GradeNotify.java deleted file mode 100644 index a554bea3..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/notifies/GradeNotify.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.services.notifies; - -import android.annotation.TargetApi; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.content.Context; - -import io.github.wulkanowy.R; - -public class GradeNotify extends NotificationService { - - private static final String CHANNEL_ID = "Grade_Notify"; - - public GradeNotify(Context context) { - super(context); - } - - @Override - @TargetApi(26) - void createChannel() { - String channelName = getString(R.string.notify_grade_channel); - - NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, channelName, - NotificationManager.IMPORTANCE_HIGH); - notificationChannel.enableLights(true); - notificationChannel.enableVibration(true); - notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); - getManager().createNotificationChannel(notificationChannel); - } - - @Override - String getChannelId() { - return CHANNEL_ID; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/notifies/NotificationService.java b/app/src/main/java/io/github/wulkanowy/services/notifies/NotificationService.java deleted file mode 100644 index 156de2bd..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/notifies/NotificationService.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.wulkanowy.services.notifies; - - -import android.annotation.TargetApi; -import android.app.Notification; -import android.app.NotificationManager; -import android.content.Context; -import android.os.Build; -import android.support.annotation.StringRes; -import android.support.v4.app.NotificationCompat; - -import java.util.Random; - -public class NotificationService { - - private NotificationManager manager; - - private Context context; - - public NotificationService(Context context) { - this.context = context; - } - - public void notify(Notification notification) { - getManager().notify(new Random().nextInt(1000), notification); - } - - public NotificationCompat.Builder notificationBuilder() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createChannel(); - } - return new NotificationCompat.Builder(context, getChannelId()); - } - - public void cancelAll() { - getManager().cancelAll(); - } - - NotificationManager getManager() { - if (manager == null) { - manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - } - return manager; - } - - String getString(@StringRes int stringId) { - return context.getString(stringId); - } - - @TargetApi(26) - void createChannel() { - } - - String getChannelId() { - return null; - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt new file mode 100644 index 00000000..7f700bac --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt @@ -0,0 +1,55 @@ +package io.github.wulkanowy.services.sync + +import android.os.Build.VERSION.SDK_INT +import android.os.Build.VERSION_CODES.O +import androidx.work.BackoffPolicy.EXPONENTIAL +import androidx.work.Constraints +import androidx.work.ExistingPeriodicWorkPolicy.KEEP +import androidx.work.ExistingPeriodicWorkPolicy.REPLACE +import androidx.work.NetworkType.CONNECTED +import androidx.work.NetworkType.UNMETERED +import androidx.work.PeriodicWorkRequest +import androidx.work.WorkManager +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.services.sync.channels.DebugChannel +import io.github.wulkanowy.services.sync.channels.NewEntriesChannel +import io.github.wulkanowy.utils.isHolidays +import org.threeten.bp.LocalDate.now +import timber.log.Timber +import java.util.concurrent.TimeUnit.MINUTES +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton + +@Singleton +class SyncManager @Inject constructor( + private val workManager: WorkManager, + private val preferencesRepository: PreferencesRepository, + newEntriesChannel: NewEntriesChannel, + debugChannel: DebugChannel, + @Named("isDebug") isDebug: Boolean +) { + + init { + if (SDK_INT >= O) newEntriesChannel.create() + if (SDK_INT >= O && isDebug) debugChannel.create() + if (now().isHolidays) stopSyncWorker() + Timber.i("SyncManager was initialized") + } + + fun startSyncWorker(restart: Boolean = false) { + if (preferencesRepository.isServiceEnabled && !now().isHolidays) { + workManager.enqueueUniquePeriodicWork(SyncWorker::class.java.simpleName, if (restart) REPLACE else KEEP, + PeriodicWorkRequest.Builder(SyncWorker::class.java, preferencesRepository.servicesInterval, MINUTES, 10, MINUTES) + .setBackoffCriteria(EXPONENTIAL, 30, MINUTES) + .setConstraints(Constraints.Builder() + .setRequiredNetworkType(if (preferencesRepository.isServicesOnlyWifi) UNMETERED else CONNECTED) + .build()) + .build()) + } + } + + fun stopSyncWorker() { + workManager.cancelUniqueWork(SyncWorker::class.java.simpleName) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt new file mode 100644 index 00000000..6a4c4ca6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt @@ -0,0 +1,81 @@ +package io.github.wulkanowy.services.sync + +import android.content.Context +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationCompat.BigTextStyle +import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT +import androidx.core.app.NotificationManagerCompat +import androidx.work.ListenableWorker +import androidx.work.RxWorker +import androidx.work.WorkerParameters +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import io.github.wulkanowy.R +import io.github.wulkanowy.api.interceptor.FeatureDisabledException +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.services.sync.channels.DebugChannel +import io.github.wulkanowy.services.sync.works.Work +import io.github.wulkanowy.utils.getCompatColor +import io.reactivex.Completable +import io.reactivex.Single +import timber.log.Timber +import kotlin.random.Random + +class SyncWorker @AssistedInject constructor( + @Assisted appContext: Context, + @Assisted workerParameters: WorkerParameters, + private val studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val works: Set<@JvmSuppressWildcards Work>, + private val preferencesRepository: PreferencesRepository, + private val notificationManager: NotificationManagerCompat +) : RxWorker(appContext, workerParameters) { + + override fun createWork(): Single { + Timber.i("SyncWorker is starting") + return studentRepository.isCurrentStudentSet() + .filter { true } + .flatMap { studentRepository.getCurrentStudent().toMaybe() } + .flatMapCompletable { student -> + semesterRepository.getCurrentSemester(student, true) + .flatMapCompletable { semester -> + Completable.mergeDelayError(works.map { work -> + work.create(student, semester) + .doOnSubscribe { Timber.i("${work::class.java.simpleName} is starting") } + .doOnError { Timber.i("${work::class.java.simpleName} result: An exception occurred") } + .doOnComplete { Timber.i("${work::class.java.simpleName} result: Success") } + }) + } + } + .toSingleDefault(Result.success()) + .onErrorReturn { + Timber.e(it, "There was an error during synchronization") + if (it is FeatureDisabledException) Result.success() + else Result.retry() + } + .doOnSuccess { + if (preferencesRepository.isDebugNotificationEnable) notify(it) + Timber.i("SyncWorker result: $it") + } + } + + private fun notify(result: Result) { + notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(applicationContext, DebugChannel.CHANNEL_ID) + .setContentTitle("Debug notification") + .setSmallIcon(R.drawable.ic_more_settings_24dp) + .setAutoCancel(true) + .setColor(applicationContext.getCompatColor(R.color.colorPrimary)) + .setStyle(BigTextStyle().bigText("${SyncWorker::class.java.simpleName} result: $result")) + .setPriority(PRIORITY_DEFAULT) + .build()) + } + + @AssistedInject.Factory + interface Factory { + + fun create(appContext: Context, workerParameters: WorkerParameters): ListenableWorker + } +} + diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorkerFactory.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorkerFactory.kt new file mode 100644 index 00000000..aadfc27f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorkerFactory.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.services.sync + +import android.content.Context +import androidx.work.ListenableWorker +import androidx.work.WorkerFactory +import androidx.work.WorkerParameters +import timber.log.Timber +import javax.inject.Inject + +class SyncWorkerFactory @Inject constructor(private val syncWorkerFactory: SyncWorker.Factory) : WorkerFactory() { + + override fun createWorker(appContext: Context, workerClassName: String, workerParameters: WorkerParameters): ListenableWorker? { + return if (workerClassName == SyncWorker::class.java.name) { + syncWorkerFactory.create(appContext, workerParameters) + } else { + Timber.e(IllegalArgumentException("Unknown worker class name: $workerClassName")) + null + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt new file mode 100644 index 00000000..02fff569 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt @@ -0,0 +1,28 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification.VISIBILITY_PUBLIC +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.NotificationManager.IMPORTANCE_DEFAULT +import android.content.Context +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class DebugChannel @Inject constructor( + private val notificationManager: NotificationManager, + private val context: Context +) { + + companion object { + const val CHANNEL_ID = "debug_channel" + } + + fun create() { + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_debug), IMPORTANCE_DEFAULT).apply { + lockscreenVisibility = VISIBILITY_PUBLIC + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewEntriesChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewEntriesChannel.kt new file mode 100644 index 00000000..8e24a2a6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewEntriesChannel.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification.VISIBILITY_PUBLIC +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.NotificationManager.IMPORTANCE_HIGH +import android.content.Context +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class NewEntriesChannel @Inject constructor( + private val notificationManager: NotificationManager, + private val context: Context +) { + + companion object { + const val CHANNEL_ID = "new_entries_channel" + } + + fun create() { + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_entries), IMPORTANCE_HIGH).apply { + enableLights(true) + enableVibration(true) + lockscreenVisibility = VISIBILITY_PUBLIC + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt new file mode 100644 index 00000000..01978c5b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.attendancesummary.AttendanceSummaryRepository +import io.reactivex.Completable +import javax.inject.Inject + +class AttendanceSummaryWork @Inject constructor( + private val attendanceSummaryRepository: AttendanceSummaryRepository +) : Work { + + override fun create(student: Student, semester: Semester): Completable { + return attendanceSummaryRepository.getAttendanceSummary(semester, -1, true).ignoreElement() + } +} + diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt new file mode 100644 index 00000000..e4b55b0e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository +import io.github.wulkanowy.utils.friday +import io.github.wulkanowy.utils.monday +import io.reactivex.Completable +import org.threeten.bp.LocalDate.now +import javax.inject.Inject + +class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work { + + override fun create(student: Student, semester: Semester): Completable { + return attendanceRepository.getAttendance(semester, now().monday, now().friday, true) + .ignoreElement() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt new file mode 100644 index 00000000..29642ad6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository +import io.github.wulkanowy.utils.friday +import io.github.wulkanowy.utils.monday +import io.reactivex.Completable +import org.threeten.bp.LocalDate.now +import javax.inject.Inject + +class CompletedLessonWork @Inject constructor( + private val completedLessonsRepository: CompletedLessonsRepository +) : Work { + + override fun create(student: Student, semester: Semester): Completable { + return completedLessonsRepository.getCompletedLessons(semester, now().monday, now().friday, true) + .ignoreElement() + } +} + diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt new file mode 100644 index 00000000..8744fcc7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.exam.ExamRepository +import io.github.wulkanowy.utils.friday +import io.github.wulkanowy.utils.monday +import io.reactivex.Completable +import org.threeten.bp.LocalDate.now +import javax.inject.Inject + +class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work { + + override fun create(student: Student, semester: Semester): Completable { + return examRepository.getExams(semester, now().monday, now().friday, true).ignoreElement() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt new file mode 100644 index 00000000..1de39a95 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.gradestatistics.GradeStatisticsRepository +import io.reactivex.Completable +import javax.inject.Inject + +class GradeStatisticsWork @Inject constructor(private val gradeStatisticsRepository: GradeStatisticsRepository) : Work { + + override fun create(student: Student, semester: Semester): Completable { + return gradeStatisticsRepository.getGradesStatistics(semester, "Wszystkie", false, true) + .ignoreElement() + } +} + diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt new file mode 100644 index 00000000..6de0bc5b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt @@ -0,0 +1,14 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository +import io.reactivex.Completable +import javax.inject.Inject + +class GradeSummaryWork @Inject constructor(private val gradeSummaryRepository: GradeSummaryRepository) : Work { + + override fun create(student: Student, semester: Semester): Completable { + return gradeSummaryRepository.getGradesSummary(semester, true).ignoreElement() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt new file mode 100644 index 00000000..0c70b476 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt @@ -0,0 +1,61 @@ +package io.github.wulkanowy.services.sync.works + +import android.app.PendingIntent +import android.app.PendingIntent.FLAG_UPDATE_CURRENT +import android.content.Context +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationCompat.DEFAULT_ALL +import androidx.core.app.NotificationCompat.PRIORITY_HIGH +import androidx.core.app.NotificationManagerCompat +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.grade.GradeRepository +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.services.sync.channels.NewEntriesChannel +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView.MenuView +import io.github.wulkanowy.utils.getCompatColor +import io.reactivex.Completable +import javax.inject.Inject +import kotlin.random.Random + +class GradeWork @Inject constructor( + private val context: Context, + private val notificationManager: NotificationManagerCompat, + private val gradeRepository: GradeRepository, + private val preferencesRepository: PreferencesRepository +) : Work { + + override fun create(student: Student, semester: Semester): Completable { + return gradeRepository.getGrades(student, semester, true, preferencesRepository.isNotificationsEnable) + .flatMap { gradeRepository.getNotNotifiedGrades(semester) } + .flatMapCompletable { + if (it.isNotEmpty()) notify(it) + gradeRepository.updateGrades(it.onEach { grade -> grade.isNotified = true }) + } + } + + private fun notify(grades: List) { + notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID) + .setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items, grades.size, grades.size)) + .setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items, grades.size, grades.size)) + .setSmallIcon(R.drawable.ic_stat_notify_grade) + .setAutoCancel(true) + .setPriority(PRIORITY_HIGH) + .setDefaults(DEFAULT_ALL) + .setColor(context.getCompatColor(R.color.colorPrimary)) + .setContentIntent( + PendingIntent.getActivity(context, MenuView.GRADE.id, + MainActivity.getStartIntent(context, MenuView.GRADE, true), FLAG_UPDATE_CURRENT)) + .setStyle(NotificationCompat.InboxStyle().run { + setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, grades.size, grades.size)) + grades.forEach { addLine("${it.subject}: ${it.entry}") } + this + }) + .build() + ) + } +} + diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt new file mode 100644 index 00000000..32b356c6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.homework.HomeworkRepository +import io.github.wulkanowy.utils.friday +import io.github.wulkanowy.utils.monday +import io.reactivex.Completable +import org.threeten.bp.LocalDate.now +import javax.inject.Inject + +class HomeworkWork @Inject constructor(private val homeworkRepository: HomeworkRepository) : Work { + + override fun create(student: Student, semester: Semester): Completable { + return homeworkRepository.getHomework(semester, now().monday, now().friday, true).ignoreElement() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt new file mode 100644 index 00000000..ebd536b7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.services.sync.works + +import android.app.PendingIntent +import android.app.PendingIntent.FLAG_UPDATE_CURRENT +import android.content.Context +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationCompat.DEFAULT_ALL +import androidx.core.app.NotificationCompat.PRIORITY_HIGH +import androidx.core.app.NotificationManagerCompat +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.services.sync.channels.NewEntriesChannel +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView.MenuView +import io.github.wulkanowy.utils.getCompatColor +import io.reactivex.Completable +import javax.inject.Inject +import kotlin.random.Random + +class LuckyNumberWork @Inject constructor( + private val context: Context, + private val notificationManager: NotificationManagerCompat, + private val luckyNumberRepository: LuckyNumberRepository, + private val preferencesRepository: PreferencesRepository +) : Work { + + override fun create(student: Student, semester: Semester): Completable { + return luckyNumberRepository.getLuckyNumber(semester, true, preferencesRepository.isNotificationsEnable) + .flatMap { luckyNumberRepository.getNotNotifiedLuckyNumber(semester) } + .flatMapCompletable { + notify(it) + luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true }) + } + } + + private fun notify(luckyNumber: LuckyNumber) { + notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID) + .setContentTitle(context.getString(R.string.lucky_number_notify_new_item_title)) + .setContentText(context.getString(R.string.lucky_number_notify_new_item, luckyNumber.luckyNumber)) + .setSmallIcon(R.drawable.ic_stat_notify_lucky_number) + .setAutoCancel(true) + .setDefaults(DEFAULT_ALL) + .setPriority(PRIORITY_HIGH) + .setColor(context.getCompatColor(R.color.colorPrimary)) + .setContentIntent( + PendingIntent.getActivity(context, MenuView.MESSAGE.id, + MainActivity.getStartIntent(context, MenuView.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT)) + .build()) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt new file mode 100644 index 00000000..d5016aee --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt @@ -0,0 +1,61 @@ +package io.github.wulkanowy.services.sync.works + +import android.app.PendingIntent +import android.app.PendingIntent.FLAG_UPDATE_CURRENT +import android.content.Context +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationCompat.DEFAULT_ALL +import androidx.core.app.NotificationCompat.PRIORITY_HIGH +import androidx.core.app.NotificationManagerCompat +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED +import io.github.wulkanowy.data.repositories.message.MessageRepository +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.services.sync.channels.NewEntriesChannel +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView.MenuView +import io.github.wulkanowy.utils.getCompatColor +import io.reactivex.Completable +import javax.inject.Inject +import kotlin.random.Random + +class MessageWork @Inject constructor( + private val context: Context, + private val notificationManager: NotificationManagerCompat, + private val messageRepository: MessageRepository, + private val preferencesRepository: PreferencesRepository +) : Work { + + override fun create(student: Student, semester: Semester): Completable { + return messageRepository.getMessages(student, RECEIVED, true, preferencesRepository.isNotificationsEnable) + .flatMap { messageRepository.getNotNotifiedMessages(student) } + .flatMapCompletable { + if (it.isNotEmpty()) notify(it) + messageRepository.updateMessages(it.onEach { message -> message.isNotified = true }) + } + } + + private fun notify(messages: List) { + notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID) + .setContentTitle(context.resources.getQuantityString(R.plurals.message_new_items, messages.size, messages.size)) + .setContentText(context.resources.getQuantityString(R.plurals.message_notify_new_items, messages.size, messages.size)) + .setSmallIcon(R.drawable.ic_stat_notify_message) + .setAutoCancel(true) + .setDefaults(DEFAULT_ALL) + .setPriority(PRIORITY_HIGH) + .setColor(context.getCompatColor(R.color.colorPrimary)) + .setContentIntent( + PendingIntent.getActivity(context, MenuView.MESSAGE.id, + MainActivity.getStartIntent(context, MenuView.MESSAGE, true), FLAG_UPDATE_CURRENT) + ) + .setStyle(NotificationCompat.InboxStyle().run { + setSummaryText(context.resources.getQuantityString(R.plurals.message_number_item, messages.size, messages.size)) + messages.forEach { addLine("${it.sender}: ${it.subject}") } + this + }) + .build()) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt new file mode 100644 index 00000000..86e540c4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt @@ -0,0 +1,60 @@ +package io.github.wulkanowy.services.sync.works + +import android.app.PendingIntent +import android.app.PendingIntent.FLAG_UPDATE_CURRENT +import android.content.Context +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationCompat.DEFAULT_ALL +import androidx.core.app.NotificationCompat.PRIORITY_HIGH +import androidx.core.app.NotificationManagerCompat +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.note.NoteRepository +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.services.sync.channels.NewEntriesChannel +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView.MenuView +import io.github.wulkanowy.utils.getCompatColor +import io.reactivex.Completable +import javax.inject.Inject +import kotlin.random.Random + +class NoteWork @Inject constructor( + private val context: Context, + private val notificationManager: NotificationManagerCompat, + private val noteRepository: NoteRepository, + private val preferencesRepository: PreferencesRepository +) : Work { + + override fun create(student: Student, semester: Semester): Completable { + return noteRepository.getNotes(student, semester, true, preferencesRepository.isNotificationsEnable) + .flatMap { noteRepository.getNotNotifiedNotes(student) } + .flatMapCompletable { + if (it.isNotEmpty()) notify(it) + noteRepository.updateNotes(it.onEach { note -> note.isNotified = true }) + } + } + + private fun notify(notes: List) { + notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID) + .setContentTitle(context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size)) + .setContentText(context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size)) + .setSmallIcon(R.drawable.ic_stat_notify_note) + .setAutoCancel(true) + .setDefaults(DEFAULT_ALL) + .setPriority(PRIORITY_HIGH) + .setColor(context.getCompatColor(R.color.colorPrimary)) + .setContentIntent( + PendingIntent.getActivity(context, MenuView.NOTE.id, + MainActivity.getStartIntent(context, MenuView.NOTE, true), FLAG_UPDATE_CURRENT)) + .setStyle(NotificationCompat.InboxStyle().run { + setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size)) + notes.forEach { addLine("${it.teacher}: ${it.category}") } + this + }) + .build()) + } +} + diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt new file mode 100644 index 00000000..fa610dee --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt @@ -0,0 +1,24 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.recipient.RecipientRepository +import io.github.wulkanowy.data.repositories.reportingunit.ReportingUnitRepository +import io.reactivex.Completable +import javax.inject.Inject + +class RecipientWork @Inject constructor( + private val reportingUnitRepository: ReportingUnitRepository, + private val recipientRepository: RecipientRepository +) : Work { + + override fun create(student: Student, semester: Semester): Completable { + return reportingUnitRepository.getReportingUnits(student, true) + .flatMapCompletable { units -> + Completable.mergeDelayError(units.map { + recipientRepository.getRecipients(student, 2, it, true).ignoreElement() + }) + } + } +} + diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt new file mode 100644 index 00000000..743ae0e8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.timetable.TimetableRepository +import io.github.wulkanowy.utils.friday +import io.github.wulkanowy.utils.monday +import io.reactivex.Completable +import org.threeten.bp.LocalDate.now +import javax.inject.Inject + +class TimetableWork @Inject constructor(private val timetableRepository: TimetableRepository) : Work { + + override fun create(student: Student, semester: Semester): Completable { + return timetableRepository.getTimetable(semester, now().monday, now().friday, true) + .ignoreElement() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt new file mode 100644 index 00000000..1601a103 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt @@ -0,0 +1,11 @@ +package io.github.wulkanowy.services.sync.works + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.reactivex.Completable + +interface Work { + + fun create(student: Student, semester: Semester): Completable +} + diff --git a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt new file mode 100644 index 00000000..f3429457 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt @@ -0,0 +1,35 @@ +package io.github.wulkanowy.services.widgets + +import android.content.Intent +import android.widget.RemoteViewsService +import dagger.android.AndroidInjection +import io.github.wulkanowy.data.db.SharedPrefHelper +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.timetable.TimetableRepository +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetFactory +import io.github.wulkanowy.utils.SchedulersProvider +import javax.inject.Inject + +class TimetableWidgetService : RemoteViewsService() { + + @Inject + lateinit var timetableRepo: TimetableRepository + + @Inject + lateinit var studentRepo: StudentRepository + + @Inject + lateinit var semesterRepo: SemesterRepository + + @Inject + lateinit var sharedPref: SharedPrefHelper + + @Inject + lateinit var schedulers: SchedulersProvider + + override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory { + AndroidInjection.inject(this) + return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, sharedPref, schedulers, applicationContext, intent) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetServices.java b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetServices.java deleted file mode 100644 index f442542e..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetServices.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.wulkanowy.services.widgets; - -import android.content.Intent; -import android.widget.RemoteViewsService; - -import javax.inject.Inject; - -import dagger.android.AndroidInjection; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.ui.widgets.TimetableWidgetFactory; - -public class TimetableWidgetServices extends RemoteViewsService { - - @Inject - RepositoryContract repository; - - @Override - public RemoteViewsFactory onGetViewFactory(Intent intent) { - AndroidInjection.inject(this); - return new TimetableWidgetFactory(getApplicationContext(), repository); - } -} 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 deleted file mode 100644 index 82ab9d19..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.wulkanowy.ui.base; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.Snackbar; -import android.support.v7.app.AppCompatDelegate; -import android.view.View; - -import butterknife.ButterKnife; -import butterknife.Unbinder; -import dagger.android.support.DaggerAppCompatActivity; -import io.github.wulkanowy.R; -import io.github.wulkanowy.utils.NetworkUtils; - -public abstract class BaseActivity extends DaggerAppCompatActivity implements BaseContract.View { - - private Unbinder unbinder; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); - } - - protected void injectViews() { - unbinder = ButterKnife.bind(this); - } - - @Override - public void showMessage(@NonNull String text) { - if (getMessageView() != null) { - Snackbar.make(getMessageView(), text, Snackbar.LENGTH_LONG).show(); - } - } - - @Override - public void showNoNetworkMessage() { - showMessage(getString(R.string.noInternet_text)); - } - - @Override - public boolean isNetworkConnected() { - return NetworkUtils.isOnline(getApplicationContext()); - } - - protected View getMessageView() { - return null; - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (unbinder != null) { - unbinder.unbind(); - } - invalidateOptionsMenu(); - } -} 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 new file mode 100644 index 00000000..0879aa3c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt @@ -0,0 +1,82 @@ +package io.github.wulkanowy.ui.base + +import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.app.AppCompatDelegate +import androidx.fragment.app.Fragment +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.snackbar.Snackbar.LENGTH_LONG +import dagger.android.AndroidInjection +import dagger.android.DispatchingAndroidInjector +import dagger.android.support.HasSupportFragmentInjector +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.utils.FragmentLifecycleLogger +import javax.inject.Inject + +abstract class BaseActivity> : AppCompatActivity(), BaseView, + HasSupportFragmentInjector { + + @Inject + lateinit var supportFragmentInjector: DispatchingAndroidInjector + + @Inject + lateinit var fragmentLifecycleLogger: FragmentLifecycleLogger + + @Inject + lateinit var themeManager: ThemeManager + + protected var messageContainer: View? = null + + abstract var presenter: T + + public override fun onCreate(savedInstanceState: Bundle?) { + AndroidInjection.inject(this) + themeManager.applyTheme(this) + super.onCreate(savedInstanceState) + supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true) + AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) + } + + override fun showError(text: String, error: Throwable) { + if (messageContainer != null) { + Snackbar.make(messageContainer!!, text, LENGTH_LONG) + .setAction(R.string.all_details) { + ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString()) + } + .show() + } else showMessage(text) + } + + override fun showMessage(text: String) { + if (messageContainer != null) Snackbar.make(messageContainer!!, text, LENGTH_LONG).show() + else Toast.makeText(this, text, Toast.LENGTH_LONG).show() + } + + override fun showExpiredDialog() { + AlertDialog.Builder(this) + .setTitle(R.string.main_session_expired) + .setMessage(R.string.main_session_relogin) + .setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onExpiredLoginSelected() } + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .show() + } + + override fun openClearLoginView() { + startActivity(LoginActivity.getStartIntent(this) + .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) }) + } + + override fun onDestroy() { + super.onDestroy() + invalidateOptionsMenu() + presenter.onDetachView() + } + + override fun supportFragmentInjector() = supportFragmentInjector +} 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 deleted file mode 100644 index 3bfa40d1..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseContract.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.wulkanowy.ui.base; - -import android.support.annotation.NonNull; - -public interface BaseContract { - - interface View { - - void showMessage(@NonNull String text); - - void showNoNetworkMessage(); - - boolean isNetworkConnected(); - } - - interface Presenter { - - void attachView(@NonNull V view); - - void detachView(); - } -} 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 deleted file mode 100644 index e2a5a9e0..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.github.wulkanowy.ui.base; - -import android.support.annotation.NonNull; -import android.support.annotation.StringRes; -import android.view.View; - -import butterknife.ButterKnife; -import butterknife.Unbinder; -import dagger.android.support.DaggerFragment; -import io.github.wulkanowy.utils.NetworkUtils; - -public abstract class BaseFragment extends DaggerFragment implements BaseContract.View { - - private Unbinder unbinder; - - protected void injectViews(@NonNull View view) { - unbinder = ButterKnife.bind(this, view); - } - - @Override - public void onDestroyView() { - if (unbinder != null) { - unbinder.unbind(); - } - super.onDestroyView(); - } - - public void setTitle(String title) { - if (getActivity() != null) { - getActivity().setTitle(title); - } - } - - @Override - public void showMessage(@NonNull String text) { - if (getActivity() != null) { - ((BaseActivity) getActivity()).showMessage(text); - } - } - - public void showMessage(@StringRes int stringId) { - showMessage(getString(stringId)); - } - - @Override - public void showNoNetworkMessage() { - if (getActivity() != null) { - ((BaseActivity) getActivity()).showNoNetworkMessage(); - } - } - - @Override - public boolean isNetworkConnected() { - return NetworkUtils.isOnline(getContext()); - } -} 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 new file mode 100644 index 00000000..4b2ad053 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt @@ -0,0 +1,40 @@ +package io.github.wulkanowy.ui.base + +import android.view.View +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.snackbar.Snackbar.LENGTH_LONG +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) { + Snackbar.make(messageContainer!!, text, LENGTH_LONG) + .setAction(R.string.all_details) { + if (isAdded) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) + } + .show() + } else { + (activity as? BaseActivity<*>)?.showError(text, error) + } + } + + override fun showMessage(text: String) { + if (messageContainer != null) { + Snackbar.make(messageContainer!!, text, LENGTH_LONG).show() + } else { + (activity as? BaseActivity<*>)?.showMessage(text) + } + } + + override fun showExpiredDialog() { + (activity as? BaseActivity<*>)?.showExpiredDialog() + } + + override fun openClearLoginView() { + (activity as? BaseActivity<*>)?.openClearLoginView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt new file mode 100644 index 00000000..fcf7e980 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.ui.base + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentPagerAdapter + +class BaseFragmentPagerAdapter(private val fragmentManager: FragmentManager) : FragmentPagerAdapter(fragmentManager) { + + private val pages = mutableMapOf() + + var containerId = 0 + + fun getFragmentInstance(position: Int): Fragment? { + if (containerId == 0) throw IllegalArgumentException("Container id is 0") + return fragmentManager.findFragmentByTag("android:switcher:$containerId:$position") + } + + fun addFragments(fragments: List) { + fragments.forEach { pages[it] = null } + } + + fun addFragmentsWithTitle(pages: Map) { + this.pages.putAll(pages) + } + + override fun getItem(position: Int) = pages.keys.elementAt(position) + + override fun getCount() = pages.size + + override fun getPageTitle(position: Int) = pages.values.elementAt(position) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BasePagerAdapter.java b/app/src/main/java/io/github/wulkanowy/ui/base/BasePagerAdapter.java deleted file mode 100644 index 178f0110..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BasePagerAdapter.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.wulkanowy.ui.base; - -import android.support.annotation.NonNull; -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 BasePagerAdapter extends FragmentStatePagerAdapter { - - private List fragmentList = new ArrayList<>(); - - private List titleList = new ArrayList<>(); - - public BasePagerAdapter(FragmentManager fragmentManager) { - super(fragmentManager); - } - - public void addFragment(@NonNull Fragment fragment, @NonNull String title) { - fragmentList.add(fragment); - titleList.add(title); - } - - public void addFragment(@NonNull Fragment fragment) { - fragmentList.add(fragment); - } - - @Override - public Fragment getItem(int position) { - return fragmentList.get(position); - } - - @Override - public int getCount() { - return fragmentList.size(); - } - - @Override - public CharSequence getPageTitle(int position) { - if (!titleList.isEmpty()) { - return titleList.get(position); - } - return null; - } -} 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 deleted file mode 100644 index 8f9b8af0..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.ui.base; - -import android.support.annotation.NonNull; - -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 attachView(@NonNull V view) { - this.view = view; - } - - @Override - public void detachView() { - view = null; - } - - protected boolean isViewAttached() { - return view != null; - } - - public final RepositoryContract getRepository() { - return repository; - } - - public V getView() { - return view; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt new file mode 100644 index 00000000..9f0b4047 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt @@ -0,0 +1,55 @@ +package io.github.wulkanowy.ui.base + +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.utils.SchedulersProvider +import io.reactivex.Completable +import io.reactivex.disposables.CompositeDisposable +import timber.log.Timber + +open class BasePresenter( + protected val errorHandler: ErrorHandler, + protected val studentRepository: StudentRepository, + protected val schedulers: SchedulersProvider +) { + + val disposable = CompositeDisposable() + + var view: T? = null + + open fun onAttachView(view: T) { + this.view = view + errorHandler.apply { + showErrorMessage = view::showError + onSessionExpired = view::showExpiredDialog + onNoCurrentStudent = view::openClearLoginView + } + } + + fun onExpiredLoginSelected() { + Timber.i("Attempt to switch the student after the session expires") + disposable.add(studentRepository.getCurrentStudent(false) + .flatMapCompletable { studentRepository.logoutStudent(it) } + .andThen(studentRepository.getSavedStudents(false)) + .flatMapCompletable { + if (it.isNotEmpty()) { + Timber.i("Switching current student") + studentRepository.switchStudent(it[0]) + } else Completable.complete() + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + Timber.i("Switch student result: Open login view") + view?.openClearLoginView() + }, { + Timber.i("Switch student result: An exception occurred") + errorHandler.dispatch(it) + })) + } + + 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 new file mode 100644 index 00000000..7681263b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.ui.base + +interface BaseView { + + fun showError(text: String, error: Throwable) + + fun showMessage(text: String) + + fun showExpiredDialog() + + fun openClearLoginView() +} 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 new file mode 100644 index 00000000..db3918b9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt @@ -0,0 +1,60 @@ +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 new file mode 100644 index 00000000..16e9a048 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt @@ -0,0 +1,51 @@ +package io.github.wulkanowy.ui.base + +import android.content.res.Resources +import com.readystatesoftware.chuck.api.ChuckCollector +import io.github.wulkanowy.R +import io.github.wulkanowy.api.interceptor.FeatureDisabledException +import io.github.wulkanowy.api.interceptor.ServiceUnavailableException +import io.github.wulkanowy.api.login.BadCredentialsException +import io.github.wulkanowy.api.login.NotLoggedInException +import io.github.wulkanowy.data.exceptions.NoCurrentStudentException +import io.github.wulkanowy.utils.security.ScramblerException +import timber.log.Timber +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import javax.inject.Inject + +open class ErrorHandler @Inject constructor(protected val resources: Resources, private val chuckCollector: ChuckCollector) { + + var showErrorMessage: (String, Throwable) -> Unit = { _, _ -> } + + var onSessionExpired: () -> Unit = {} + + var onNoCurrentStudent: () -> Unit = {} + + fun dispatch(error: Throwable) { + chuckCollector.onError(error.javaClass.simpleName, error) + Timber.e(error, "An exception occurred while the Wulkanowy was running") + proceed(error) + } + + protected open fun proceed(error: Throwable) { + resources.run { + when (error) { + is UnknownHostException -> showErrorMessage(getString(R.string.error_no_internet), error) + is SocketTimeoutException -> showErrorMessage(getString(R.string.error_timeout), error) + is NotLoggedInException -> showErrorMessage(getString(R.string.error_login_failed), error) + is ServiceUnavailableException -> showErrorMessage(getString(R.string.error_service_unavailable), error) + is FeatureDisabledException -> showErrorMessage(getString(R.string.error_feature_disabled), error) + is ScramblerException, is BadCredentialsException -> onSessionExpired() + is NoCurrentStudentException -> onNoCurrentStudent() + else -> showErrorMessage(getString(R.string.error_unknown), error) + } + } + } + + open fun clear() { + showErrorMessage = { _, _ -> } + onSessionExpired = {} + onNoCurrentStudent = {} + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt new file mode 100644 index 00000000..449f451b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.ui.base + +import android.content.pm.PackageManager.GET_ACTIVITIES +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES +import io.github.wulkanowy.R +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import javax.inject.Inject + +class ThemeManager @Inject constructor(private val preferencesRepository: PreferencesRepository) { + + fun applyTheme(activity: AppCompatActivity) { + if (isThemeApplicable(activity)) { + activity.delegate.apply { + when (preferencesRepository.appTheme) { + "light" -> setLocalNightMode(MODE_NIGHT_NO) + "dark" -> setLocalNightMode(MODE_NIGHT_YES) + "black" -> { + setLocalNightMode(MODE_NIGHT_YES) + activity.setTheme(R.style.WulkanowyTheme_Black) + } + } + } + } + } + + private fun isThemeApplicable(activity: AppCompatActivity): Boolean { + return activity.packageManager.getPackageInfo(activity.packageName, GET_ACTIVITIES) + .activities.singleOrNull { it.name == activity::class.java.canonicalName }?.theme + .let { it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar } + } +} 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 deleted file mode 100644 index a0960af2..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java +++ /dev/null @@ -1,236 +0,0 @@ -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.annotation.NonNull; -import android.support.design.widget.TextInputLayout; -import android.support.v7.app.ActionBar; -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 android.widget.Toast; - -import javax.inject.Inject; - -import butterknife.BindView; -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); - injectViews(); - - presenter.attachView(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 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 showActionBar(boolean show) { - ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - if (show) { - actionBar.show(); - } else { - actionBar.hide(); - } - } - } - - @NonNull - @Override - protected View getMessageView() { - return findViewById(R.id.login_activity_container); - } - - - @Override - public void onSyncFailed() { - Toast.makeText(getApplicationContext(), R.string.login_sync_error, Toast.LENGTH_LONG).show(); - } - - private void onLoginProgressUpdate(String step, String message) { - loginProgressText.setText(String.format("%1$s/2 - %2$s...", step, message)); - } - - @Override - public void onDestroy() { - presenter.detachView(); - super.onDestroy(); - } -} 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 deleted file mode 100644 index 96bb49d6..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/login/LoginContract.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.wulkanowy.ui.login; - -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(); - - void showActionBar(boolean show); - - void onSyncFailed(); - - } - - interface Presenter extends BaseContract.Presenter { - - void attemptLogin(String email, String password, String symbol); - - void onStartAsync(); - - void onDoInBackground(int stepNumber) throws Exception; - - void onLoginProgress(int step); - - void onEndAsync(int success, Exception exception); - - void onCanceledAsync(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginModule.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginModule.java deleted file mode 100644 index 0cb26c49..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/login/LoginModule.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.ui.login; - -import dagger.Binds; -import dagger.Module; -import io.github.wulkanowy.di.scopes.PerActivity; - -@Module -public abstract class LoginModule { - - @PerActivity - @Binds - abstract LoginContract.Presenter provideLoginPresenter(LoginPresenter loginPresenter); -} 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 deleted file mode 100644 index d8461bf3..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/login/LoginPresenter.java +++ /dev/null @@ -1,178 +0,0 @@ -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; -import io.github.wulkanowy.utils.FabricUtils; - -public class LoginPresenter extends BasePresenter - implements LoginContract.Presenter { - - private LoginTask loginAsync; - - private String email; - - private String password; - - private String symbol; - - @Inject - LoginPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void attemptLogin(String email, String password, String symbol) { - getView().resetViewErrors(); - - this.email = email; - this.password = password; - this.symbol = getNormalizedSymbol(symbol); - - if (!isAllFieldCorrect(password, email)) { - getView().showSoftInput(); - return; - } - - if (getView().isNetworkConnected()) { - loginAsync = new LoginTask(this); - loginAsync.execute(); - - } else { - getView().showNoNetworkMessage(); - } - - getView().hideSoftInput(); - } - - @Override - public void onStartAsync() { - if (isViewAttached()) { - getView().showActionBar(false); - getView().showLoginProgress(true); - } - } - - @Override - public void onDoInBackground(int stepNumber) throws Exception { - switch (stepNumber) { - case 1: - getRepository().getSyncRepo().registerUser(email, password, symbol); - break; - case 2: - getRepository().getSyncRepo().syncAll(); - break; - } - } - - @Override - public void onLoginProgress(int step) { - if (step == 1) { - getView().setStepOneLoginProgress(); - } else if (step == 2) { - getView().setStepTwoLoginProgress(); - } - } - - @Override - public void onEndAsync(int success, Exception exception) { - switch (success) { - case LoginTask.LOGIN_AND_SYNC_SUCCESS: - FabricUtils.logRegister(true, getRepository().getDbRepo().getCurrentSymbol().getSymbol(), "Success"); - getView().openMainActivity(); - return; - case LoginTask.SYNC_FAILED: - FabricUtils.logRegister(true, symbol, exception.getMessage()); - getView().onSyncFailed(); - getView().openMainActivity(); - return; - case LoginTask.LOGIN_FAILED: - if (exception instanceof BadCredentialsException) { - getView().setErrorPassIncorrect(); - getView().showSoftInput(); - } else if (exception instanceof AccountPermissionException) { - getView().setErrorSymbolRequired(); - getView().showSoftInput(); - } else { - FabricUtils.logRegister(false, symbol, exception.getMessage()); - getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception)); - } - break; - } - getView().showActionBar(true); - getView().showLoginProgress(false); - } - - @Override - public void onCanceledAsync() { - if (isViewAttached()) { - getView().showActionBar(true); - 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().getResRepo().getSymbolsKeysArray(); - String[] values = getRepository().getResRepo().getSymbolsValuesArray(); - LinkedHashMap map = new LinkedHashMap<>(); - - for (int i = 0; i < Math.min(keys.length, values.length); ++i) { - map.put(keys[i], values[i]); - } - - if (map.containsKey(symbol)) { - return map.get(symbol); - } - return AppConstant.DEFAULT_SYMBOL; - } - - private boolean isAllFieldCorrect(String password, String email) { - boolean correct = true; - - if (TextUtils.isEmpty(password)) { - getView().setErrorPassRequired(); - correct = false; - } else if (!isPasswordValid(password)) { - getView().setErrorPassInvalid(); - correct = false; - } - - if (TextUtils.isEmpty(email)) { - getView().setErrorEmailRequired(); - correct = false; - } else if (!isEmailValid(email)) { - getView().setErrorEmailInvalid(); - correct = false; - } - return correct; - } - - @Override - public void detachView() { - if (loginAsync != null) { - loginAsync.cancel(true); - loginAsync = null; - } - super.detachView(); - } -} 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 deleted file mode 100644 index 2938a836..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/login/LoginTask.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.ui.login; - -import android.os.AsyncTask; - -public class LoginTask extends AsyncTask { - - public final static int LOGIN_AND_SYNC_SUCCESS = 1; - - public final static int LOGIN_FAILED = -1; - - public final static int SYNC_FAILED = 2; - - private LoginContract.Presenter presenter; - - private Exception exception; - - LoginTask(LoginContract.Presenter presenter) { - this.presenter = presenter; - } - - @Override - protected void onPreExecute() { - presenter.onStartAsync(); - } - - @Override - protected Integer doInBackground(Void... params) { - try { - publishProgress(1); - presenter.onDoInBackground(1); - } catch (Exception e) { - exception = e; - return LOGIN_FAILED; - } - - try { - publishProgress(2); - presenter.onDoInBackground(2); - } catch (Exception e) { - exception = e; - return SYNC_FAILED; - } - return LOGIN_AND_SYNC_SUCCESS; - } - - @Override - protected void onProgressUpdate(Integer... progress) { - presenter.onLoginProgress(progress[0]); - } - - @Override - protected void onPostExecute(Integer 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 deleted file mode 100644 index b739ae98..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java +++ /dev/null @@ -1,170 +0,0 @@ -package io.github.wulkanowy.ui.main; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.design.widget.AppBarLayout; -import android.support.v7.app.ActionBar; -import android.support.v7.widget.Toolbar; -import android.view.View; - -import com.aurelhubert.ahbottomnavigation.AHBottomNavigation; -import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem; -import com.aurelhubert.ahbottomnavigation.AHBottomNavigationViewPager; - -import javax.inject.Inject; -import javax.inject.Named; - -import butterknife.BindView; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.services.jobs.SyncJob; -import io.github.wulkanowy.ui.base.BaseActivity; -import io.github.wulkanowy.ui.base.BasePagerAdapter; -import io.github.wulkanowy.ui.main.attendance.AttendanceFragment; -import io.github.wulkanowy.ui.main.exams.ExamsFragment; -import io.github.wulkanowy.ui.main.grades.GradesFragment; -import io.github.wulkanowy.ui.main.settings.SettingsFragment; -import io.github.wulkanowy.ui.main.timetable.TimetableFragment; -import io.github.wulkanowy.utils.CommonUtils; - -public class MainActivity extends BaseActivity implements MainContract.View, - AHBottomNavigation.OnTabSelectedListener, OnFragmentIsReadyListener { - - public static final String EXTRA_CARD_ID_KEY = "cardId"; - - @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; - - @BindView(R.id.main_activity_appbar) - AppBarLayout appBar; - - @Named("Main") - @Inject - BasePagerAdapter pagerAdapter; - - @Inject - MainContract.Presenter presenter; - - @Inject - RepositoryContract repository; - - 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); - setSupportActionBar((Toolbar) findViewById(R.id.main_activity_toolbar)); - injectViews(); - - presenter.attachView(this, getIntent().getIntExtra(EXTRA_CARD_ID_KEY, -1)); - } - - @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); - appBar.setExpanded(true, true); - invalidateOptionsMenu(); - return true; - } - - @Override - public void setCurrentPage(int position) { - viewPager.setCurrentItem(position, false); - } - - @Override - public void onFragmentIsReady() { - presenter.onFragmentIsReady(); - } - - @Override - public void initiationBottomNav(int tabPosition) { - bottomNavigation.addItem(new AHBottomNavigationItem(getString(R.string.grades_text), - R.drawable.ic_menu_grade_26dp)); - - bottomNavigation.addItem(new AHBottomNavigationItem(getString(R.string.attendance_text), - R.drawable.ic_menu_attendance_24dp)); - - bottomNavigation.addItem(new AHBottomNavigationItem(getString(R.string.exams_text), - R.drawable.ic_menu_exams_24dp)); - - bottomNavigation.addItem(new AHBottomNavigationItem(getString(R.string.timetable_text), - R.drawable.ic_menu_timetable_24dp)); - - bottomNavigation.addItem(new AHBottomNavigationItem(getString(R.string.settings_text), - R.drawable.ic_menu_other_24dp)); - - bottomNavigation.setAccentColor(getResources().getColor(R.color.colorPrimary)); - bottomNavigation.setInactiveColor(CommonUtils.getThemeAttrColor(this, android.R.attr.textColorTertiary)); - bottomNavigation.setDefaultBackgroundColor(CommonUtils.getThemeAttrColor(this, R.attr.bottomNavBackground)); - bottomNavigation.setTitleState(AHBottomNavigation.TitleState.ALWAYS_SHOW); - bottomNavigation.setOnTabSelectedListener(this); - bottomNavigation.setCurrentItem(tabPosition); - bottomNavigation.setBehaviorTranslationEnabled(false); - } - - @Override - public void initiationViewPager(int tabPosition) { - pagerAdapter.addFragment(new GradesFragment()); - pagerAdapter.addFragment(new AttendanceFragment()); - pagerAdapter.addFragment(new ExamsFragment()); - pagerAdapter.addFragment(new TimetableFragment()); - pagerAdapter.addFragment(new SettingsFragment()); - - viewPager.setPagingEnabled(false); - viewPager.setAdapter(pagerAdapter); - viewPager.setOffscreenPageLimit(4); - viewPager.setCurrentItem(tabPosition, false); - } - - @Override - public void startSyncService(int interval, boolean useOnlyWifi) { - SyncJob.start(getApplicationContext(), interval, useOnlyWifi); - } - - @NonNull - @Override - protected View getMessageView() { - return findViewById(R.id.main_activity_view_pager); - } - - @Override - protected void onDestroy() { - presenter.detachView(); - super.onDestroy(); - } -} 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 deleted file mode 100644 index cb4e29e4..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/MainContract.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.ui.main; - -import android.support.annotation.NonNull; - -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(); - - void initiationViewPager(int tabPosition); - - void initiationBottomNav(int tabPosition); - - void startSyncService(int interval, boolean useOnlyWifi); - } - - interface Presenter extends BaseContract.Presenter { - - void attachView(@NonNull View view, int initTabId); - - void onTabSelected(int position, boolean wasSelected); - - void onFragmentIsReady(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/MainModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/MainModule.java deleted file mode 100644 index ff3fed67..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/MainModule.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.main; - -import javax.inject.Named; - -import dagger.Binds; -import dagger.Module; -import dagger.Provides; -import dagger.android.ContributesAndroidInjector; -import io.github.wulkanowy.di.scopes.PerActivity; -import io.github.wulkanowy.di.scopes.PerFragment; -import io.github.wulkanowy.ui.base.BasePagerAdapter; -import io.github.wulkanowy.ui.main.attendance.AttendanceFragment; -import io.github.wulkanowy.ui.main.attendance.AttendanceModule; -import io.github.wulkanowy.ui.main.exams.ExamsFragment; -import io.github.wulkanowy.ui.main.exams.ExamsModule; -import io.github.wulkanowy.ui.main.grades.GradesFragment; -import io.github.wulkanowy.ui.main.grades.GradesModule; -import io.github.wulkanowy.ui.main.timetable.TimetableFragment; -import io.github.wulkanowy.ui.main.timetable.TimetableModule; - -@Module -public abstract class MainModule { - - @PerActivity - @Binds - abstract MainContract.Presenter provideMainPresenter(MainPresenter mainPresenter); - - @Named("Main") - @PerActivity - @Provides - static BasePagerAdapter provideAdapter(MainActivity activity) { - return new BasePagerAdapter(activity.getSupportFragmentManager()); - } - - @PerFragment - @ContributesAndroidInjector(modules = GradesModule.class) - abstract GradesFragment bindsGradesFragment(); - - @PerFragment - @ContributesAndroidInjector(modules = TimetableModule.class) - abstract TimetableFragment bindTimetableFragment(); - - @PerFragment - @ContributesAndroidInjector(modules = ExamsModule.class) - abstract ExamsFragment bindExamsFragment(); - - @PerFragment - @ContributesAndroidInjector(modules = AttendanceModule.class) - abstract AttendanceFragment bindAttendanceFramgnet(); -} 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 deleted file mode 100644 index 2ca9eefa..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/MainPresenter.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.wulkanowy.ui.main; - -import android.support.annotation.NonNull; - -import javax.inject.Inject; - -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.ui.base.BasePresenter; - -import static io.github.wulkanowy.utils.TimeUtilsKt.isHolidays; - -public class MainPresenter extends BasePresenter - implements MainContract.Presenter { - - private int fragmentCount = 0; - - @Inject - MainPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void attachView(@NonNull MainContract.View view, int initTabId) { - super.attachView(view); - getView().showProgressBar(true); - getView().hideActionBar(); - - int tabPosition; - - if (initTabId != -1) { - tabPosition = initTabId; - } else { - tabPosition = getRepository().getSharedRepo().getStartupTab(); - } - - getView().initiationBottomNav(tabPosition); - getView().initiationViewPager(tabPosition); - - if (getRepository().getSharedRepo().isServicesEnable() && !isHolidays()) { - getView().startSyncService(getRepository().getSharedRepo().getServicesInterval(), - getRepository().getSharedRepo().isMobileDisable()); - } - } - - @Override - public void onTabSelected(int position, boolean wasSelected) { - if (!wasSelected) { - getView().setCurrentPage(position); - } - } - - @Override - public void onFragmentIsReady() { - if (fragmentCount < 4) { - fragmentCount++; - } - - if (fragmentCount == 4) { - 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 deleted file mode 100644 index 21e3ba12..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/OnFragmentIsReadyListener.java +++ /dev/null @@ -1,6 +0,0 @@ -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 deleted file mode 100644 index 1ad66787..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import android.support.annotation.NonNull; - -import io.github.wulkanowy.ui.base.BaseContract; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; - -public interface AttendanceContract { - - interface View extends BaseContract.View { - - void setActivityTitle(); - - void scrollViewPagerToPosition(int position); - - void setTabDataToAdapter(String date); - - void setAdapterWithTabLayout(); - - boolean isMenuVisible(); - - void setThemeForTab(int position); - } - - interface Presenter extends BaseContract.Presenter { - - void onFragmentActivated(boolean isVisible); - - void attachView(@NonNull View view, OnFragmentIsReadyListener listener); - - void setRestoredPosition(int position); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java deleted file mode 100644 index ea38e51d..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java +++ /dev/null @@ -1,91 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.DialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; - -public class AttendanceDialogFragment extends DialogFragment { - - private static final String ARGUMENT_KEY = "Item"; - - private AttendanceLesson lesson; - - @BindView(R.id.attendance_dialog_subject_value) - TextView subject; - - @BindView(R.id.attendance_dialog_date_value) - TextView date; - - @BindView(R.id.attendance_dialog_number_value) - TextView number; - - @BindView(R.id.attendance_dialog_description_value) - TextView description; - - public AttendanceDialogFragment() { - //empty constructor for fragment - } - - public static AttendanceDialogFragment newInstance(AttendanceLesson lesson) { - AttendanceDialogFragment dialogFragment = new AttendanceDialogFragment(); - - Bundle bundle = new Bundle(); - bundle.putSerializable(ARGUMENT_KEY, lesson); - - dialogFragment.setArguments(bundle); - - return dialogFragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null) { - lesson = (AttendanceLesson) getArguments().getSerializable(ARGUMENT_KEY); - } - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.attendance_dialog, container, false); - - ButterKnife.bind(this, view); - - if (!lesson.getSubject().isEmpty()) { - subject.setText(lesson.getSubject()); - } - - if (!lesson.getDate().isEmpty()) { - date.setText(lesson.getDate()); - } - - if (0 != lesson.getNumber()) { - number.setText(String.valueOf(lesson.getNumber())); - } - - description.setText(lesson.getDescription()); - - if (lesson.getAbsenceUnexcused()) { - description.setTextColor(getResources().getColor(R.color.colorPrimary)); - } - - return view; - } - - @OnClick(R.id.attendance_dialog_close) - void onClickCloseButton() { - dismiss(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java deleted file mode 100644 index 732806db..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java +++ /dev/null @@ -1,102 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.TabLayout; -import android.support.v4.view.ViewPager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import javax.inject.Inject; -import javax.inject.Named; - -import butterknife.BindView; -import io.github.wulkanowy.R; -import io.github.wulkanowy.ui.base.BaseFragment; -import io.github.wulkanowy.ui.base.BasePagerAdapter; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; -import io.github.wulkanowy.ui.main.attendance.tab.AttendanceTabFragment; - -public class AttendanceFragment extends BaseFragment implements AttendanceContract.View { - - private static final String CURRENT_ITEM_KEY = "CurrentItem"; - - @BindView(R.id.attendance_fragment_viewpager) - ViewPager viewPager; - - @BindView(R.id.attendance_fragment_tab_layout) - TabLayout tabLayout; - - @Inject - @Named("Attendance") - BasePagerAdapter pagerAdapter; - - @Inject - AttendanceContract.Presenter presenter; - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_attendance, container, false); - injectViews(view); - - presenter.attachView(this, (OnFragmentIsReadyListener) getActivity()); - - if (savedInstanceState != null) { - presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY)); - } - return view; - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (presenter != null) { - presenter.onFragmentActivated(menuVisible); - } - } - - @Override - public void setTabDataToAdapter(String date) { - pagerAdapter.addFragment(AttendanceTabFragment.newInstance(date), date); - } - - @Override - public void setAdapterWithTabLayout() { - viewPager.setAdapter(pagerAdapter); - tabLayout.setupWithViewPager(viewPager); - } - - @Override - public void scrollViewPagerToPosition(int position) { - viewPager.setCurrentItem(position, false); - } - - @Override - public void setThemeForTab(int position) { - TabLayout.Tab tab = tabLayout.getTabAt(position); - if (tab != null) { - tab.setCustomView(R.layout.current_week_tab); - } - } - - @Override - public void setActivityTitle() { - setTitle(getString(R.string.attendance_text)); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem()); - super.onSaveInstanceState(outState); - } - - @Override - public void onDestroyView() { - presenter.detachView(); - super.onDestroyView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceModule.java deleted file mode 100644 index 8679cf10..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceModule.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import javax.inject.Named; - -import dagger.Binds; -import dagger.Module; -import dagger.Provides; -import dagger.android.ContributesAndroidInjector; -import io.github.wulkanowy.di.scopes.PerChildFragment; -import io.github.wulkanowy.di.scopes.PerFragment; -import io.github.wulkanowy.ui.base.BasePagerAdapter; -import io.github.wulkanowy.ui.main.attendance.tab.AttendanceTabFragment; -import io.github.wulkanowy.ui.main.attendance.tab.AttendanceTabModule; - -@Module -public abstract class AttendanceModule { - - @PerFragment - @Binds - abstract AttendanceContract.Presenter provideAttendancePresenter(AttendancePresenter attendancePresenter); - - @PerFragment - @Named("Attendance") - @Provides - static BasePagerAdapter providePagerAdapter(AttendanceFragment fragment) { - return new BasePagerAdapter(fragment.getChildFragmentManager()); - } - - @PerChildFragment - @ContributesAndroidInjector(modules = AttendanceTabModule.class) - abstract AttendanceTabFragment bindAttendanceTabFragment(); -} 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 deleted file mode 100644 index 73a2837c..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java +++ /dev/null @@ -1,108 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance; - -import android.support.annotation.NonNull; - -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.async.AbstractTask; -import io.github.wulkanowy.utils.async.AsyncListeners; - -import static io.github.wulkanowy.utils.TimeUtilsKt.getFirstDayOfCurrentWeek; -import static io.github.wulkanowy.utils.TimeUtilsKt.getMondaysFromCurrentSchoolYear; - -public class AttendancePresenter extends BasePresenter - implements AttendanceContract.Presenter, AsyncListeners.OnFirstLoadingListener { - - private AbstractTask loadingTask; - - private List dates = new ArrayList<>(); - - private OnFragmentIsReadyListener listener; - - private int positionToScroll = 0; - - private boolean isFirstSight = false; - - @Inject - AttendancePresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void attachView(@NonNull AttendanceContract.View view, OnFragmentIsReadyListener listener) { - super.attachView(view); - this.listener = listener; - - if (getView().isMenuVisible()) { - getView().setActivityTitle(); - } - - if (dates.isEmpty()) { - dates = getMondaysFromCurrentSchoolYear(); - } - - if (positionToScroll == 0) { - positionToScroll = dates.indexOf(getFirstDayOfCurrentWeek()); - } - - if (!isFirstSight) { - isFirstSight = true; - - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - } - } - - @Override - public void onFragmentActivated(boolean isVisible) { - if (isVisible) { - getView().setActivityTitle(); - } - } - - @Override - public void onDoInBackgroundLoading() { - for (String date : dates) { - getView().setTabDataToAdapter(date); - } - } - - @Override - public void onCanceledLoadingAsync() { - //do nothing - - } - - @Override - public void onEndLoadingAsync(boolean result, Exception exception) { - if (result) { - getView().setAdapterWithTabLayout(); - getView().setThemeForTab(positionToScroll); - getView().scrollViewPagerToPosition(positionToScroll); - listener.onFragmentIsReady(); - } - } - - @Override - public void setRestoredPosition(int position) { - this.positionToScroll = position; - } - - @Override - public void detachView() { - isFirstSight = false; - - if (loadingTask != null) { - loadingTask.cancel(true); - loadingTask = null; - } - super.detachView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceHeader.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceHeader.java deleted file mode 100644 index 86128109..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceHeader.java +++ /dev/null @@ -1,151 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance.tab; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -import java.util.List; - -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem; -import eu.davidea.flexibleadapter.items.IFlexible; -import eu.davidea.viewholders.ExpandableViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.Day; -import io.github.wulkanowy.utils.CommonUtils; - -public class AttendanceHeader - extends AbstractExpandableHeaderItem { - - private Day day; - - AttendanceHeader(Day day) { - this.day = day; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - AttendanceHeader that = (AttendanceHeader) o; - - return new EqualsBuilder() - .append(day, that.day) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(day) - .toHashCode(); - } - - @Override - public int getLayoutRes() { - return R.layout.attendance_header; - } - - @Override - public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new HeaderViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, - int position, List payloads) { - holder.onBind(day, getSubItems()); - } - - static class HeaderViewHolder extends ExpandableViewHolder { - - @BindView(R.id.attendance_header_day) - TextView dayName; - - @BindView(R.id.attendance_header_date) - TextView date; - - @BindView(R.id.attendance_header_description) - TextView description; - - @BindView(R.id.attendance_header_alert_image) - ImageView alert; - - @BindView(R.id.attendance_header_free_name) - TextView freeName; - - private Context context; - - HeaderViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - view.setOnClickListener(this); - ButterKnife.bind(this, view); - context = view.getContext(); - } - - void onBind(Day item, List subItems) { - dayName.setText(StringUtils.capitalize(item.getDayName())); - date.setText(item.getDate()); - - int numberOfHours = countNotPresentHours(subItems); - description.setText((getContentView().getResources().getQuantityString(R.plurals.numberOfAbsences, - numberOfHours, numberOfHours))); - description.setVisibility(numberOfHours > 0 ? View.VISIBLE : View.INVISIBLE); - alert.setVisibility(isSubItemsHasChanges(subItems) ? View.VISIBLE : View.INVISIBLE); - freeName.setVisibility(subItems.isEmpty() ? View.VISIBLE : View.INVISIBLE); - setInactiveHeader(item.getAttendanceLessons().isEmpty()); - } - - private void setInactiveHeader(boolean inactive) { - ((FrameLayout) getContentView()).setForeground(inactive ? null : getSelectableDrawable()); - dayName.setTextColor(CommonUtils.getThemeAttrColor(context, - inactive ? android.R.attr.textColorSecondary : android.R.attr.textColorPrimary)); - - if (inactive) { - getContentView().setBackgroundColor(CommonUtils.getThemeAttrColor(context, R.attr.colorControlHighlight)); - } else { - getContentView().setBackgroundDrawable(context.getResources().getDrawable(R.drawable.ic_border)); - } - } - - private Drawable getSelectableDrawable() { - int[] attrs = new int[]{R.attr.selectableItemBackground}; - TypedArray typedArray = context.obtainStyledAttributes(attrs); - Drawable drawable = typedArray.getDrawable(0); - typedArray.recycle(); - return drawable; - } - - private int countNotPresentHours(List subItems) { - int i = 0; - for (AttendanceSubItem subItem : subItems) { - if (subItem.getLesson().getAbsenceUnexcused()) { - i++; - } - } - return i; - } - - private boolean isSubItemsHasChanges(List subItems) { - for (AttendanceSubItem subItem : subItems) { - if (subItem.getLesson().getAbsenceUnexcused() || subItem.getLesson() - .getUnexcusedLateness()) { - return true; - } - } - return false; - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceSubItem.java deleted file mode 100644 index 65c8329e..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceSubItem.java +++ /dev/null @@ -1,122 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance.tab; - -import android.content.Context; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.FragmentActivity; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -import java.util.List; - -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.items.AbstractSectionableItem; -import eu.davidea.flexibleadapter.items.IFlexible; -import eu.davidea.viewholders.FlexibleViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; -import io.github.wulkanowy.ui.main.attendance.AttendanceDialogFragment; - -class AttendanceSubItem - extends AbstractSectionableItem { - - private AttendanceLesson lesson; - - AttendanceSubItem(AttendanceHeader header, AttendanceLesson lesson) { - super(header); - this.lesson = lesson; - } - - AttendanceLesson getLesson() { - return lesson; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - AttendanceSubItem that = (AttendanceSubItem) o; - - return new EqualsBuilder() - .append(lesson, that.lesson) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(lesson) - .toHashCode(); - } - - @Override - public int getLayoutRes() { - return R.layout.attendance_subitem; - } - - @Override - public 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.attendance_subItem_lesson) - TextView lessonName; - - @BindView(R.id.attendance_subItem_number) - TextView lessonNumber; - - @BindView(R.id.attendance_subItem_description) - TextView lessonDescription; - - @BindView(R.id.attendance_subItem_alert_image) - ImageView alert; - - private Context context; - - private AttendanceLesson item; - - SubItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - context = view.getContext(); - view.setOnClickListener(this); - } - - void onBind(AttendanceLesson lesson) { - item = lesson; - - lessonName.setText(lesson.getSubject()); - lessonNumber.setText((String.valueOf(lesson.getNumber()))); - lessonDescription.setText(lesson.getDescription()); - alert.setVisibility(lesson.getAbsenceUnexcused() || lesson.getUnexcusedLateness() - ? View.VISIBLE : View.INVISIBLE); - } - - @Override - public void onClick(View view) { - super.onClick(view); - showDialog(); - } - - private void showDialog() { - AttendanceDialogFragment dialogFragment = AttendanceDialogFragment.newInstance(item); - dialogFragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0); - dialogFragment.show(((FragmentActivity) context).getSupportFragmentManager(), item.toString()); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabContract.java deleted file mode 100644 index a83e67e8..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabContract.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance.tab; - -import java.util.List; - -import io.github.wulkanowy.ui.base.BaseContract; - -public interface AttendanceTabContract { - - interface View extends BaseContract.View { - - void updateAdapterList(List headerItems); - - void onRefreshSuccess(); - - void hideRefreshingBar(); - - void showNoItem(boolean show); - - void showProgressBar(boolean show); - } - - interface Presenter extends BaseContract.Presenter { - - void onFragmentActivated(boolean isSelected); - - void setArgumentDate(String date); - - void onRefresh(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabFragment.java deleted file mode 100644 index 8de47469..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabFragment.java +++ /dev/null @@ -1,129 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance.tab; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.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 eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; -import io.github.wulkanowy.R; -import io.github.wulkanowy.ui.base.BaseFragment; - -public class AttendanceTabFragment extends BaseFragment implements AttendanceTabContract.View, - SwipeRefreshLayout.OnRefreshListener { - - private static final String ARGUMENT_KEY = "date"; - - @BindView(R.id.attendance_tab_fragment_recycler) - RecyclerView recyclerView; - - @BindView(R.id.attendance_tab_fragment_swipe_refresh) - SwipeRefreshLayout refreshLayout; - - @BindView(R.id.attendance_tab_fragment_progress_bar) - View progressBar; - - @BindView(R.id.attendance_tab_fragment_no_item_container) - View noItemView; - - @Inject - AttendanceTabContract.Presenter presenter; - - @Inject - FlexibleAdapter adapter; - - private boolean isFragmentVisible = false; - - public static AttendanceTabFragment newInstance(String date) { - AttendanceTabFragment fragmentTab = new AttendanceTabFragment(); - - 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_attendance_tab, container, false); - injectViews(view); - - if (getArguments() != null) { - presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY)); - } - - presenter.attachView(this); - presenter.onFragmentActivated(isFragmentVisible); - return view; - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - adapter.setAutoCollapseOnExpand(true); - adapter.setAutoScrollOnExpand(true); - adapter.expandItemsAtStartUp(); - - recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.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); - isFragmentVisible = menuVisible; - if (presenter != null) { - presenter.onFragmentActivated(menuVisible); - } - } - - @Override - public void onRefresh() { - presenter.onRefresh(); - } - - @Override - public void onRefreshSuccess() { - showMessage(R.string.sync_completed); - } - - @Override - public void hideRefreshingBar() { - refreshLayout.setRefreshing(false); - } - - @Override - public void showProgressBar(boolean show) { - progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE); - } - - @Override - public void showNoItem(boolean show) { - noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); - } - - @Override - public void onDestroyView() { - presenter.detachView(); - super.onDestroyView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabModule.java deleted file mode 100644 index 380a221f..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabModule.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance.tab; - -import dagger.Binds; -import dagger.Module; -import dagger.Provides; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import io.github.wulkanowy.di.scopes.PerChildFragment; - -@Module -public abstract class AttendanceTabModule { - - @PerChildFragment - @Binds - abstract AttendanceTabContract.Presenter provideAttendanceTabPresenter(AttendanceTabPresenter attendanceTabPresenter); - - @PerChildFragment - @Provides - static FlexibleAdapter provideAdapter() { - return new FlexibleAdapter<>(null); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabPresenter.java deleted file mode 100644 index 7d0c26e5..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabPresenter.java +++ /dev/null @@ -1,194 +0,0 @@ -package io.github.wulkanowy.ui.main.attendance.tab; - -import android.support.annotation.NonNull; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; -import io.github.wulkanowy.data.db.dao.entities.Day; -import io.github.wulkanowy.data.db.dao.entities.Week; -import io.github.wulkanowy.ui.base.BasePresenter; -import io.github.wulkanowy.utils.FabricUtils; -import io.github.wulkanowy.utils.async.AbstractTask; -import io.github.wulkanowy.utils.async.AsyncListeners; - -public class AttendanceTabPresenter extends BasePresenter - implements AttendanceTabContract.Presenter, AsyncListeners.OnRefreshListener, - AsyncListeners.OnFirstLoadingListener { - - private AbstractTask refreshTask; - - private AbstractTask loadingTask; - - private List headerItems = new ArrayList<>(); - - private String date; - - private boolean isFirstSight = false; - - @Inject - AttendanceTabPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void attachView(@NonNull AttendanceTabContract.View view) { - super.attachView(view); - - getView().showProgressBar(true); - getView().showNoItem(false); - } - - @Override - public void onFragmentActivated(boolean isSelected) { - if (!isFirstSight && isSelected && isViewAttached()) { - isFirstSight = true; - - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - } else if (!isSelected) { - cancelAsyncTasks(); - } - } - - @Override - public void onRefresh() { - if (getView().isNetworkConnected()) { - refreshTask = new AbstractTask(); - refreshTask.setOnRefreshListener(this); - refreshTask.execute(); - } else { - getView().showNoNetworkMessage(); - getView().hideRefreshingBar(); - } - } - - @Override - public void onDoInBackgroundRefresh() throws Exception { - syncData(); - } - - @Override - public void onCanceledRefreshAsync() { - if (isViewAttached()) { - getView().hideRefreshingBar(); - } - } - - @Override - public void onEndRefreshAsync(boolean result, Exception exception) { - if (result) { - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - - getView().onRefreshSuccess(); - } else { - getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception)); - } - getView().hideRefreshingBar(); - - FabricUtils.logRefresh("Attendance", result, date); - } - - @Override - public void onDoInBackgroundLoading() throws Exception { - Week week = getRepository().getDbRepo().getWeek(date); - boolean isShowPresent = getRepository().getSharedRepo().isShowAttendancePresent(); - - if (week == null || !week.getAttendanceSynced()) { - syncData(); - week = getRepository().getDbRepo().getWeek(date); - } - - week.resetDayList(); - List dayList = week.getDayList(); - - headerItems = new ArrayList<>(); - - boolean isEmptyWeek = true; - - for (Day day : dayList) { - day.resetAttendanceLessons(); - AttendanceHeader headerItem = new AttendanceHeader(day); - - if (isEmptyWeek) { - isEmptyWeek = day.getAttendanceLessons().isEmpty(); - } - - List lessonList = day.getAttendanceLessons(); - - List subItems = new ArrayList<>(); - - for (AttendanceLesson lesson : lessonList) { - if (!isShowPresent && lesson.getPresence()) { - continue; - } - - lesson.setDescription(getRepository().getResRepo().getAttendanceLessonDescription(lesson)); - subItems.add(new AttendanceSubItem(headerItem, lesson)); - } - - if (!isShowPresent && subItems.isEmpty()) { - continue; - } - - headerItem.setSubItems(subItems); - headerItem.setExpanded(false); - headerItems.add(headerItem); - } - - if (isEmptyWeek) { - headerItems = new ArrayList<>(); - } - } - - @Override - public void onCanceledLoadingAsync() { - // do nothing - } - - @Override - public void onEndLoadingAsync(boolean result, Exception exception) { - if (headerItems.isEmpty()) { - getView().showNoItem(true); - getView().updateAdapterList(null); - } else { - getView().updateAdapterList(headerItems); - getView().showNoItem(false); - } - getView().showProgressBar(false); - } - - @Override - public void setArgumentDate(String date) { - this.date = date; - } - - private void syncData() throws Exception { - getRepository().getSyncRepo().syncAttendance(0, date); - } - - private void cancelAsyncTasks() { - if (refreshTask != null) { - refreshTask.cancel(true); - refreshTask = null; - } - if (loadingTask != null) { - loadingTask.cancel(true); - loadingTask = null; - } - } - - @Override - public void detachView() { - cancelAsyncTasks(); - isFirstSight = false; - super.detachView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsContract.java deleted file mode 100644 index aeeecea8..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsContract.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.ui.main.exams; - -import android.support.annotation.NonNull; - -import io.github.wulkanowy.ui.base.BaseContract; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; - -public interface ExamsContract { - - interface View extends BaseContract.View { - - void setActivityTitle(); - - boolean isMenuVisible(); - - void scrollViewPagerToPosition(int position); - - void setTabDataToAdapter(String date); - - void setAdapterWithTabLayout(); - - void setThemeForTab(int position); - } - - interface Presenter extends BaseContract.Presenter { - - void attachView(@NonNull View view, OnFragmentIsReadyListener listener); - - void onFragmentActivated(boolean isVisible); - - void setRestoredPosition(int position); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsDialogFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsDialogFragment.java deleted file mode 100644 index 8c4fafcd..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsDialogFragment.java +++ /dev/null @@ -1,81 +0,0 @@ -package io.github.wulkanowy.ui.main.exams; - - -import android.os.Bundle; -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.Exam; - -public class ExamsDialogFragment extends DialogFragment { - - private static final String ARGUMENT_KEY = "Item"; - - private Exam exam; - - @BindView(R.id.exams_dialog_subject_value) - TextView subject; - - @BindView(R.id.exams_dialog_type_value) - TextView type; - - @BindView(R.id.exams_dialog_teacher_value) - TextView teacher; - - @BindView(R.id.exams_dialog_date_value) - TextView entryDate; - - @BindView(R.id.exams_dialog_description_value) - TextView description; - - public static ExamsDialogFragment newInstance(Exam exam) { - ExamsDialogFragment dialogFragment = new ExamsDialogFragment(); - - Bundle bundle = new Bundle(); - bundle.putSerializable(ARGUMENT_KEY, exam); - - dialogFragment.setArguments(bundle); - - return dialogFragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null) { - exam = (Exam) getArguments().getSerializable(ARGUMENT_KEY); - } - } - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.exams_dialog, container, false); - - ButterKnife.bind(this, view); - - subject.setText(exam.getSubjectAndGroup()); - teacher.setText(exam.getTeacher()); - type.setText(exam.getType()); - entryDate.setText(exam.getEntryDate()); - - if (!exam.getDescription().isEmpty()) { - description.setText(exam.getDescription()); - } - - return view; - } - - @OnClick(R.id.exams_dialog_close) - void onClickCloseButton() { - dismiss(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsFragment.java deleted file mode 100644 index 05557b81..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsFragment.java +++ /dev/null @@ -1,100 +0,0 @@ -package io.github.wulkanowy.ui.main.exams; - -import android.os.Bundle; -import android.support.annotation.NonNull; -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 javax.inject.Named; - -import butterknife.BindView; -import io.github.wulkanowy.R; -import io.github.wulkanowy.ui.base.BaseFragment; -import io.github.wulkanowy.ui.base.BasePagerAdapter; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; -import io.github.wulkanowy.ui.main.exams.tab.ExamsTabFragment; - -public class ExamsFragment extends BaseFragment implements ExamsContract.View { - - private static final String CURRENT_ITEM_KEY = "CurrentItem"; - - @BindView(R.id.exams_fragment_viewpager) - ViewPager viewPager; - - @BindView(R.id.exams_fragment_tab_layout) - TabLayout tabLayout; - - @Inject - @Named("Exams") - BasePagerAdapter pagerAdapter; - - @Inject - ExamsContract.Presenter presenter; - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_exams, container, false); - injectViews(view); - - presenter.attachView(this, (OnFragmentIsReadyListener) getActivity()); - - if (savedInstanceState != null) { - presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY)); - } - return view; - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (presenter != null) { - presenter.onFragmentActivated(menuVisible); - } - } - - @Override - public void setActivityTitle() { - setTitle(getString(R.string.exams_text)); - } - - @Override - public void scrollViewPagerToPosition(int position) { - viewPager.setCurrentItem(position, false); - } - - @Override - public void setThemeForTab(int position) { - TabLayout.Tab tab = tabLayout.getTabAt(position); - if (tab != null) { - tab.setCustomView(R.layout.current_week_tab); - } - } - - @Override - public void setTabDataToAdapter(String date) { - pagerAdapter.addFragment(ExamsTabFragment.newInstance(date), date); - } - - @Override - public void setAdapterWithTabLayout() { - viewPager.setAdapter(pagerAdapter); - tabLayout.setupWithViewPager(viewPager); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem()); - super.onSaveInstanceState(outState); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - presenter.detachView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsModule.java deleted file mode 100644 index 8d56cf23..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsModule.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.ui.main.exams; - -import javax.inject.Named; - -import dagger.Binds; -import dagger.Module; -import dagger.Provides; -import dagger.android.ContributesAndroidInjector; -import io.github.wulkanowy.di.scopes.PerChildFragment; -import io.github.wulkanowy.di.scopes.PerFragment; -import io.github.wulkanowy.ui.base.BasePagerAdapter; -import io.github.wulkanowy.ui.main.exams.tab.ExamsTabFragment; -import io.github.wulkanowy.ui.main.exams.tab.ExamsTabModule; - -@Module -public abstract class ExamsModule { - - @PerFragment - @Binds - abstract ExamsContract.Presenter provideExamsPresneter(ExamsPresenter examsPresenter); - - @Named("Exams") - @PerFragment - @Provides - static BasePagerAdapter providePagerAdapter(ExamsFragment fragment) { - return new BasePagerAdapter(fragment.getChildFragmentManager()); - } - - @PerChildFragment - @ContributesAndroidInjector(modules = ExamsTabModule.class) - abstract ExamsTabFragment bindExamsTabFragment(); -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsPresenter.java deleted file mode 100644 index 20c0fe2e..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsPresenter.java +++ /dev/null @@ -1,107 +0,0 @@ -package io.github.wulkanowy.ui.main.exams; - -import android.support.annotation.NonNull; - -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.async.AbstractTask; -import io.github.wulkanowy.utils.async.AsyncListeners; - -import static io.github.wulkanowy.utils.TimeUtilsKt.getFirstDayOfCurrentWeek; -import static io.github.wulkanowy.utils.TimeUtilsKt.getMondaysFromCurrentSchoolYear; - -public class ExamsPresenter extends BasePresenter - implements ExamsContract.Presenter, AsyncListeners.OnFirstLoadingListener { - - private AbstractTask loadingTask; - - private List dates = new ArrayList<>(); - - private OnFragmentIsReadyListener listener; - - private int positionToScroll = 0; - - private boolean isFirstSight = false; - - @Inject - ExamsPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void attachView(@NonNull ExamsContract.View view, OnFragmentIsReadyListener listener) { - super.attachView(view); - this.listener = listener; - - if (getView().isMenuVisible()) { - getView().setActivityTitle(); - } - - if (dates.isEmpty()) { - dates = getMondaysFromCurrentSchoolYear(); - } - - if (positionToScroll == 0) { - positionToScroll = dates.indexOf(getFirstDayOfCurrentWeek()); - } - - if (!isFirstSight) { - isFirstSight = true; - - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - } - } - - @Override - public void onFragmentActivated(boolean isVisible) { - if (isVisible) { - getView().setActivityTitle(); - } - } - - @Override - public void setRestoredPosition(int position) { - this.positionToScroll = position; - } - - @Override - public void onDoInBackgroundLoading() { - for (String date : dates) { - getView().setTabDataToAdapter(date); - } - } - - @Override - public void onCanceledLoadingAsync() { - // do nothing - } - - @Override - public void onEndLoadingAsync(boolean result, Exception exception) { - if (result) { - getView().setAdapterWithTabLayout(); - getView().setThemeForTab(positionToScroll); - getView().scrollViewPagerToPosition(positionToScroll); - listener.onFragmentIsReady(); - } - } - - @Override - public void detachView() { - isFirstSight = false; - - if (loadingTask != null) { - loadingTask.cancel(true); - loadingTask = null; - } - super.detachView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsHeader.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsHeader.java deleted file mode 100644 index 6c0c364e..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsHeader.java +++ /dev/null @@ -1,82 +0,0 @@ -package io.github.wulkanowy.ui.main.exams.tab; - -import android.view.View; -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.AbstractHeaderItem; -import eu.davidea.flexibleadapter.items.IFlexible; -import eu.davidea.viewholders.FlexibleViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.Day; - -public class ExamsHeader extends AbstractHeaderItem { - - private Day day; - - ExamsHeader(Day day) { - this.day = day; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - ExamsHeader that = (ExamsHeader) 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.exams_header; - } - - @Override - public HeaderVieHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new HeaderVieHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, HeaderVieHolder holder, int position, List payloads) { - holder.onBind(day); - } - - static class HeaderVieHolder extends FlexibleViewHolder { - - @BindView(R.id.exams_header_name) - TextView name; - - @BindView(R.id.exams_header_date) - TextView date; - - HeaderVieHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - } - - void onBind(Day item) { - name.setText(StringUtils.capitalize(item.getDayName())); - date.setText(item.getDate()); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsSubItem.java deleted file mode 100644 index 9ff10ebc..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsSubItem.java +++ /dev/null @@ -1,109 +0,0 @@ -package io.github.wulkanowy.ui.main.exams.tab; - -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.flexibleadapter.items.IFlexible; -import eu.davidea.viewholders.FlexibleViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.Exam; -import io.github.wulkanowy.ui.main.exams.ExamsDialogFragment; - -public class ExamsSubItem - extends AbstractSectionableItem { - - private Exam exam; - - ExamsSubItem(ExamsHeader header, Exam exam) { - super(header); - this.exam = exam; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - ExamsSubItem that = (ExamsSubItem) o; - - return new EqualsBuilder() - .append(exam, that.exam) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(exam) - .toHashCode(); - } - - @Override - public int getLayoutRes() { - return R.layout.exams_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(exam); - } - - static class SubItemViewHolder extends FlexibleViewHolder { - - @BindView(R.id.exams_subitem_subject) - TextView subject; - - @BindView(R.id.exams_subitems_teacher) - TextView teacher; - - @BindView(R.id.exams_subitems_type) - TextView type; - - private Exam item; - - SubItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - view.setOnClickListener(this); - } - - void onBind(Exam exam) { - item = exam; - - subject.setText(item.getSubjectAndGroup()); - teacher.setText(item.getTeacher()); - type.setText(item.getType()); - } - - @Override - public void onClick(View view) { - super.onClick(view); - showDialog(); - - } - - private void showDialog() { - ExamsDialogFragment dialogFragment = ExamsDialogFragment.newInstance(item); - dialogFragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0); - dialogFragment.show(((FragmentActivity) getContentView().getContext()).getSupportFragmentManager(), - item.toString()); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabContract.java deleted file mode 100644 index 4e0785bb..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabContract.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.ui.main.exams.tab; - -import java.util.List; - -import io.github.wulkanowy.ui.base.BaseContract; - -public interface ExamsTabContract { - - interface View extends BaseContract.View { - - void onRefreshSuccess(); - - void hideRefreshingBar(); - - void showNoItem(boolean show); - - void showProgressBar(boolean show); - - void updateAdapterList(List headerItems); - } - - interface Presenter extends BaseContract.Presenter { - - void onFragmentActivated(boolean isSelected); - - void setArgumentDate(String date); - - void onRefresh(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabFragment.java deleted file mode 100644 index 1b6f361a..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabFragment.java +++ /dev/null @@ -1,126 +0,0 @@ -package io.github.wulkanowy.ui.main.exams.tab; - -import android.os.Bundle; -import android.support.annotation.Nullable; -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 eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; -import io.github.wulkanowy.R; -import io.github.wulkanowy.ui.base.BaseFragment; - -public class ExamsTabFragment extends BaseFragment implements ExamsTabContract.View, - SwipeRefreshLayout.OnRefreshListener { - - private static final String ARGUMENT_KEY = "date"; - - @BindView(R.id.exams_tab_fragment_recycler) - RecyclerView recyclerView; - - @BindView(R.id.exams_tab_fragment_swipe_refresh) - SwipeRefreshLayout refreshLayout; - - @BindView(R.id.exams_tab_fragment_progress_bar) - View progressBar; - - @BindView(R.id.exams_tab_fragment_no_item_container) - View noItemView; - - @Inject - ExamsTabContract.Presenter presenter; - - @Inject - FlexibleAdapter adapter; - - private boolean isFragmentVisible = false; - - public static ExamsTabFragment newInstance(String date) { - ExamsTabFragment tabFragment = new ExamsTabFragment(); - - Bundle bundle = new Bundle(); - bundle.putString(ARGUMENT_KEY, date); - tabFragment.setArguments(bundle); - - return tabFragment; - } - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_exams_tab, container, false); - injectViews(view); - - if (getArguments() != null) { - presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY)); - } - presenter.attachView(this); - presenter.onFragmentActivated(isFragmentVisible); - return view; - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - adapter.setDisplayHeadersAtStartUp(true); - - recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext())); - recyclerView.setAdapter(adapter); - - refreshLayout.setColorSchemeResources(android.R.color.black); - refreshLayout.setOnRefreshListener(this); - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - isFragmentVisible = menuVisible; - if (presenter != null) { - presenter.onFragmentActivated(menuVisible); - } - } - - @Override - public void updateAdapterList(List headerItems) { - adapter.updateDataSet(headerItems); - } - - @Override - public void onRefresh() { - presenter.onRefresh(); - } - - @Override - public void onRefreshSuccess() { - showMessage(R.string.sync_completed); - } - - @Override - public void hideRefreshingBar() { - refreshLayout.setRefreshing(false); - } - - @Override - public void showNoItem(boolean show) { - noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); - } - - @Override - public void showProgressBar(boolean show) { - progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE); - } - - - @Override - public void onDestroyView() { - presenter.detachView(); - super.onDestroyView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabModule.java deleted file mode 100644 index fc9a7595..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabModule.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.ui.main.exams.tab; - -import dagger.Binds; -import dagger.Module; -import dagger.Provides; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import io.github.wulkanowy.di.scopes.PerChildFragment; - -@Module -public abstract class ExamsTabModule { - - @PerChildFragment - @Binds - abstract ExamsTabContract.Presenter provideExamsTabPresenter(ExamsTabPresenter examsTabPresenter); - - @PerChildFragment - @Provides - static FlexibleAdapter provideAdapter() { - return new FlexibleAdapter<>(null); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabPresenter.java deleted file mode 100644 index b60a4607..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabPresenter.java +++ /dev/null @@ -1,167 +0,0 @@ -package io.github.wulkanowy.ui.main.exams.tab; - -import android.support.annotation.NonNull; - -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.Exam; -import io.github.wulkanowy.data.db.dao.entities.Week; -import io.github.wulkanowy.ui.base.BasePresenter; -import io.github.wulkanowy.utils.FabricUtils; -import io.github.wulkanowy.utils.async.AbstractTask; -import io.github.wulkanowy.utils.async.AsyncListeners; - -public class ExamsTabPresenter extends BasePresenter - implements ExamsTabContract.Presenter, AsyncListeners.OnFirstLoadingListener, - AsyncListeners.OnRefreshListener { - - private AbstractTask refreshTask; - - private AbstractTask loadingTask; - - private List subItems = new ArrayList<>(); - - private String date; - - private boolean isFirstSight = false; - - @Inject - ExamsTabPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void attachView(@NonNull ExamsTabContract.View view) { - super.attachView(view); - getView().showProgressBar(true); - getView().showNoItem(false); - } - - @Override - public void onFragmentActivated(boolean isSelected) { - if (!isFirstSight && isSelected && isViewAttached()) { - isFirstSight = true; - - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - } else if (!isSelected) { - cancelAsyncTasks(); - } - } - - @Override - public void setArgumentDate(String date) { - this.date = date; - } - - @Override - public void onRefresh() { - if (getView().isNetworkConnected()) { - refreshTask = new AbstractTask(); - refreshTask.setOnRefreshListener(this); - refreshTask.execute(); - } else { - getView().showNoNetworkMessage(); - getView().hideRefreshingBar(); - } - } - - @Override - public void onDoInBackgroundRefresh() throws Exception { - syncData(); - } - - @Override - public void onCanceledRefreshAsync() { - if (isViewAttached()) { - getView().hideRefreshingBar(); - } - } - - @Override - public void onEndRefreshAsync(boolean result, Exception exception) { - if (result) { - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - - getView().onRefreshSuccess(); - } else { - getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception)); - } - getView().hideRefreshingBar(); - - FabricUtils.logRefresh("Exams", result, date); - } - - @Override - public void onDoInBackgroundLoading() throws Exception { - Week week = getRepository().getDbRepo().getWeek(date); - - if (week == null || !week.getExamsSynced()) { - syncData(); - week = getRepository().getDbRepo().getWeek(date); - } - - week.resetDayList(); - List dayList = week.getDayList(); - - subItems = new ArrayList<>(); - - for (Day day : dayList) { - day.resetExams(); - ExamsHeader headerItem = new ExamsHeader(day); - - List examList = day.getExams(); - - for (Exam exam : examList) { - subItems.add(new ExamsSubItem(headerItem, exam)); - } - } - } - - @Override - public void onCanceledLoadingAsync() { - // do nothing - } - - @Override - public void onEndLoadingAsync(boolean result, Exception exception) { - if (subItems.isEmpty()) { - getView().showNoItem(true); - getView().updateAdapterList(null); - } else { - getView().updateAdapterList(subItems); - getView().showNoItem(false); - } - getView().showProgressBar(false); - } - - private void syncData() throws Exception { - getRepository().getSyncRepo().syncExams(0, date); - } - - private void cancelAsyncTasks() { - if (refreshTask != null) { - refreshTask.cancel(true); - refreshTask = null; - } - if (loadingTask != null) { - loadingTask.cancel(true); - loadingTask = null; - } - } - - @Override - public void detachView() { - isFirstSight = false; - cancelAsyncTasks(); - super.detachView(); - } -} 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 deleted file mode 100644 index d2423ec8..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesContract.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.github.wulkanowy.ui.main.grades; - -import android.support.annotation.NonNull; -import android.support.v4.widget.SwipeRefreshLayout; - -import java.util.List; - -import io.github.wulkanowy.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 updateSummaryAdapterList(List summarySubItems); - - void showNoItem(boolean show); - - void onRefreshSuccessNoGrade(); - - void onRefreshSuccess(int number); - - void hideRefreshingBar(); - - void setActivityTitle(); - - void setCurrentSemester(int semester); - - boolean isMenuVisible(); - - void setSummaryAverages(String calculatedValue, String predictedValue, String finalValue ); - - } - - interface Presenter extends BaseContract.Presenter { - - void onFragmentVisible(boolean isVisible); - - void onRefresh(); - - void attachView(@NonNull View view, OnFragmentIsReadyListener listener); - - void onSemesterChange(int which); - - void onSemesterSwitchActive(); - } -} 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 deleted file mode 100644 index 17ebc6e6..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesDialogFragment.java +++ /dev/null @@ -1,117 +0,0 @@ -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; -import io.github.wulkanowy.utils.GradeUtils; - -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.grades_dialog, container, false); - - ButterKnife.bind(this, view); - - subject.setText(grade.getSubject()); - value.setText(grade.getValue()); - value.setBackgroundResource(GradeUtils.getValueColor(grade.getValue())); - 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 deleted file mode 100644 index ca1b949c..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesFragment.java +++ /dev/null @@ -1,213 +0,0 @@ -package io.github.wulkanowy.ui.main.grades; - -import android.content.DialogInterface; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.RecyclerView; -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 android.widget.TextView; - -import java.util.List; - -import javax.inject.Inject; - -import butterknife.BindView; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; -import io.github.wulkanowy.R; -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_summary_container) - View summary; - - @BindView(R.id.grade_fragment_details_container) - View details; - - @BindView(R.id.grade_fragment_recycler) - RecyclerView recyclerView; - - @BindView(R.id.grade_fragment_summary_recycler) - RecyclerView summaryRecyclerView; - - @BindView(R.id.grade_fragment_no_item_container) - View noItemView; - - @BindView(R.id.grade_fragment_swipe_refresh) - SwipeRefreshLayout refreshLayout; - - @BindView(R.id.grade_fragment_summary_predicted_average) - TextView predictedAverage; - - @BindView(R.id.grade_fragment_summary_calculated_average) - TextView calculatedAverage; - - @BindView(R.id.grade_fragment_summary_final_average) - TextView finalAverage; - - @Inject - FlexibleAdapter adapter; - - @Inject - FlexibleAdapter summaryAdapter; - - @Inject - GradesContract.Presenter presenter; - - int currentSemester = -1; - - 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); - injectViews(view); - - presenter.attachView(this, (OnFragmentIsReadyListener) getActivity()); - return view; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - menu.clear(); - inflater.inflate(R.menu.grades_action_menu, menu); - super.onCreateOptionsMenu(menu, inflater); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_semester_switch: - presenter.onSemesterSwitchActive(); - CharSequence[] items = new CharSequence[]{ - getResources().getString(R.string.semester_text, 1), - getResources().getString(R.string.semester_text, 2), - }; - new AlertDialog.Builder(getContext()) - .setTitle(R.string.switch_semester) - .setNegativeButton(R.string.cancel, null) - .setSingleChoiceItems(items, this.currentSemester, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - presenter.onSemesterChange(which); - dialog.cancel(); - } - }).show(); - return true; - case R.id.action_summary_switch: - boolean isDetailsVisible = details.getVisibility() == View.VISIBLE; - - item.setTitle(isDetailsVisible ? R.string.action_title_details : R.string.action_title_summary); - details.setVisibility(isDetailsVisible ? View.INVISIBLE : View.VISIBLE); - summary.setVisibility(isDetailsVisible ? View.VISIBLE : View.INVISIBLE); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - noItemView.setVisibility(View.GONE); - summary.setVisibility(View.INVISIBLE); - details.setVisibility(View.VISIBLE); - - adapter.setAutoCollapseOnExpand(true); - adapter.setAutoScrollOnExpand(true); - adapter.expandItemsAtStartUp(); - summaryAdapter.setDisplayHeadersAtStartUp(true); - - recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext())); - recyclerView.setAdapter(adapter); - summaryRecyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.getContext())); - summaryRecyclerView.setAdapter(summaryAdapter); - summaryRecyclerView.setNestedScrollingEnabled(false); - - 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 setSummaryAverages(String calculatedValue, String predictedValue, String finalValue) { - calculatedAverage.setText(calculatedValue); - predictedAverage.setText(predictedValue); - finalAverage.setText(finalValue); - } - - @Override - public void setActivityTitle() { - setTitle(getString(R.string.grades_text)); - } - - public void setCurrentSemester(int currentSemester) { - this.currentSemester = currentSemester; - } - - @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 updateSummaryAdapterList(List summarySubItems) { - summaryAdapter.updateDataSet(summarySubItems); - } - - @Override - public void onRefreshSuccessNoGrade() { - showMessage(R.string.snackbar_no_grades); - } - - @Override - public void onRefreshSuccess(int number) { - showMessage(getString(R.string.snackbar_new_grade, number)); - } - - @Override - public void onDestroyView() { - presenter.detachView(); - super.onDestroyView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesHeader.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesHeader.java deleted file mode 100644 index 03f8564a..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesHeader.java +++ /dev/null @@ -1,199 +0,0 @@ -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.AnimationUtils; -import io.github.wulkanowy.utils.GradeUtils; - -public class GradesHeader - extends AbstractExpandableHeaderItem { - - private Subject subject; - - private final boolean isShowSummary; - - GradesHeader(Subject subject, boolean isShowSummary) { - this.subject = subject; - this.isShowSummary = isShowSummary; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - GradesHeader that = (GradesHeader) 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.grades_header; - } - - @Override - public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new HeaderViewHolder(view, adapter, isShowSummary); - } - - @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_predicted_rating_text) - TextView predictedText; - - @BindView(R.id.grade_header_final_rating_text) - TextView finalText; - - @BindView(R.id.grade_header_alert_image) - View alertImage; - - private Resources resources; - - private Subject item; - - private FlexibleAdapter adapter; - - private boolean isShowSummary; - - HeaderViewHolder(View view, FlexibleAdapter adapter, boolean isShowSummary) { - super(view, adapter); - ButterKnife.bind(this, view); - resources = view.getResources(); - view.setOnClickListener(this); - this.isShowSummary = isShowSummary; - this.adapter = adapter; - } - - void onBind(Subject item, List subItems) { - this.item = item; - - subjectName.setText(item.getName()); - numberText.setText(resources.getQuantityString(R.plurals.numberOfGradesPlurals, - subItems.size(), subItems.size())); - averageText.setText(getGradesAverageString()); - - predictedText.setText(resources.getString(R.string.info_grades_predicted_rating, - GradeUtils.getShortGradeValue(item.getPredictedRating()))); - finalText.setText(resources.getString(R.string.info_grades_final_rating, - GradeUtils.getShortGradeValue(item.getFinalRating()))); - - resetViews(); - toggleSubjectText(); - toggleSummary(); - - alertImage.setVisibility(isSubItemsReadAndSaveAlertView(subItems) - ? View.INVISIBLE : View.VISIBLE); - } - - private String getGradesAverageString() { - float average = GradeUtils.calculateWeightedAverage(item.getGradeList()); - - if (average < 0) { - return resources.getString(R.string.info_no_average); - } - - return resources.getString(R.string.info_average_grades, average); - } - - @Override - public void onClick(View view) { - super.onClick(view); - toggleSubjectText(); - toggleSummary(); - } - - private void resetViews() { - subjectName.setMaxLines(1); - setDefaultSummaryVisibility(predictedText, item.getPredictedRating()); - setDefaultSummaryVisibility(finalText, item.getFinalRating()); - } - - private void setDefaultSummaryVisibility(View view, String value) { - if (!"-".equals(value) && isShowSummary) { - view.setVisibility(View.VISIBLE); - } else { - view.setVisibility(View.GONE); - } - } - - private void toggleSubjectText() { - if (isExpand()) { - subjectName.setMaxLines(3); - } else { - subjectName.setMaxLines(1); - } - } - - private void toggleSummary() { - toggleSummaryView(predictedText, item.getPredictedRating(), isExpand()); - toggleSummaryView(finalText, item.getFinalRating(), isExpand()); - } - - private boolean isExpand() { - return adapter.isExpanded(getFlexibleAdapterPosition()); - } - - private void toggleSummaryView(View view, String value, boolean expand) { - if ("-".equals(value) || isShowSummary) { - return; - } - - if (expand) { - AnimationUtils.slideDown(view); - } else { - AnimationUtils.slideUp(view); - } - } - - private boolean isSubItemsReadAndSaveAlertView(List subItems) { - boolean isRead = true; - - for (GradesSubItem gradesSubItem : subItems) { - isRead = gradesSubItem.getGrade().getRead(); - gradesSubItem.setSubjectAlertImage(alertImage); - } - - return isRead; - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesModule.java deleted file mode 100644 index 25eeef48..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesModule.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.ui.main.grades; - -import dagger.Binds; -import dagger.Module; -import dagger.Provides; -import eu.davidea.flexibleadapter.FlexibleAdapter; - -@Module -public abstract class GradesModule { - - @Binds - abstract GradesContract.Presenter provideGradesPresenter(GradesPresenter gradesPresenter); - - @Provides - static FlexibleAdapter provideGradesAdapter() { - return new FlexibleAdapter<>(null); - } - - @Provides - static FlexibleAdapter provideGradesSummaryAdapter() { - return new FlexibleAdapter<>(null); - } -} 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 deleted file mode 100644 index 0f4d363c..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesPresenter.java +++ /dev/null @@ -1,230 +0,0 @@ -package io.github.wulkanowy.ui.main.grades; - -import android.support.annotation.NonNull; - -import com.crashlytics.android.answers.Answers; -import com.crashlytics.android.answers.CustomEvent; - -import org.threeten.bp.LocalDate; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -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.FabricUtils; -import io.github.wulkanowy.utils.GradeUtils; -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 List summarySubItems = new ArrayList<>(); - - private boolean isFirstSight = false; - - private int semesterName; - - private float finalAverage; - - private float predictedAverage; - - private float calculatedAverage; - - @Inject - GradesPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void attachView(@NonNull GradesContract.View view, OnFragmentIsReadyListener listener) { - super.attachView(view); - this.listener = listener; - - if (getView().isMenuVisible()) { - getView().setActivityTitle(); - } - - semesterName = getRepository().getDbRepo().getCurrentSemesterName(); - getView().setCurrentSemester(semesterName - 1); - - if (!isFirstSight) { - isFirstSight = true; - reloadGrades(); - } - } - - @Override - public void onSemesterSwitchActive() { - cancelAsyncTasks(); - } - - @Override - public void onSemesterChange(int which) { - semesterName = which + 1; - getView().setCurrentSemester(which); - reloadGrades(); - - Answers.getInstance().logCustom(new CustomEvent("Semester change") - .putCustomAttribute("Name", semesterName)); - } - - @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().showNoNetworkMessage(); - getView().hideRefreshingBar(); - } - } - - @Override - public void onDoInBackgroundRefresh() throws Exception { - getRepository().getSyncRepo().syncSubjects(semesterName); - getRepository().getSyncRepo().syncGrades(semesterName); - } - - @Override - public void onCanceledRefreshAsync() { - if (isViewAttached()) { - getView().hideRefreshingBar(); - } - } - - @Override - public void onEndRefreshAsync(boolean result, Exception exception) { - if (result) { - reloadGrades(); - - int numberOfNewGrades = getRepository().getDbRepo().getNewGrades(semesterName).size(); - - if (numberOfNewGrades <= 0) { - getView().onRefreshSuccessNoGrade(); - } else { - getView().onRefreshSuccess(numberOfNewGrades); - } - } else { - getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception)); - } - getView().hideRefreshingBar(); - - FabricUtils.logRefresh("Grades", result, LocalDate.now().toString()); - } - - @Override - public void onDoInBackgroundLoading() { - List subjectList = getRepository().getDbRepo().getSubjectList(semesterName); - boolean isShowSummary = getRepository().getSharedRepo().isShowGradesSummary(); - - headerItems = new ArrayList<>(); - summarySubItems = new ArrayList<>(); - - for (Subject subject : subjectList) { - subject.resetGradeList(); - List gradeList = subject.getGradeList(); - - GradesSummaryHeader summaryHeader = new GradesSummaryHeader(subject, GradeUtils.calculateWeightedAverage(gradeList)); - summarySubItems.add(new GradesSummarySubItem(summaryHeader, subject)); - - if (!gradeList.isEmpty()) { - GradesHeader headerItem = new GradesHeader(subject, isShowSummary); - - List subItems = new ArrayList<>(); - - for (Grade grade : gradeList) { - subItems.add(new GradesSubItem(headerItem, grade)); - } - - headerItem.setSubItems(subItems); - headerItem.setExpanded(false); - headerItems.add(headerItem); - } - } - - finalAverage = GradeUtils.calculateSubjectsAverage(subjectList, false); - predictedAverage = GradeUtils.calculateSubjectsAverage(subjectList, true); - calculatedAverage = GradeUtils.calculateDetailedSubjectsAverage(subjectList); - } - - @Override - public void onCanceledLoadingAsync() { - // do nothing - } - - @Override - public void onEndLoadingAsync(boolean result, Exception exception) { - getView().showNoItem(headerItems.isEmpty()); - getView().updateAdapterList(headerItems); - - setSummaryAverages(); - getView().updateSummaryAdapterList(summarySubItems); - - listener.onFragmentIsReady(); - } - - private void setSummaryAverages() { - getView().setSummaryAverages( - getFormattedAverage(calculatedAverage), - getFormattedAverage(predictedAverage), - getFormattedAverage(finalAverage) - ); - } - - private String getFormattedAverage(float average) { - if (-1.0f == average) { - return "-- --"; - } - - return String.format(Locale.FRANCE, "%.2f", average); - } - - private void reloadGrades() { - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - } - - private void cancelAsyncTasks() { - if (refreshTask != null) { - refreshTask.cancel(true); - refreshTask = null; - } - if (loadingTask != null) { - loadingTask.cancel(true); - loadingTask = null; - } - } - - @Override - public void detachView() { - isFirstSight = false; - cancelAsyncTasks(); - super.detachView(); - } -} 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 deleted file mode 100644 index ee995040..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSubItem.java +++ /dev/null @@ -1,162 +0,0 @@ -package io.github.wulkanowy.ui.main.grades; - -import android.content.Context; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.FragmentActivity; -import android.view.View; -import android.widget.TextView; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -import java.util.List; - -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.items.AbstractSectionableItem; -import eu.davidea.viewholders.FlexibleViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.Grade; -import io.github.wulkanowy.utils.GradeUtils; - -public class GradesSubItem - extends AbstractSectionableItem { - - private Grade grade; - - private static int numberOfNotReadGrade; - - private View subjectAlertImage; - - GradesSubItem(GradesHeader header, Grade grade) { - super(header); - this.grade = grade; - } - - public Grade getGrade() { - return grade; - } - - void setSubjectAlertImage(View subjectAlertImage) { - this.subjectAlertImage = subjectAlertImage; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - GradesSubItem that = (GradesSubItem) o; - - return new EqualsBuilder() - .append(grade, that.grade) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(grade) - .toHashCode(); - } - - @Override - public int getLayoutRes() { - return R.layout.grades_subitem; - } - - @Override - public SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new SubItemViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, SubItemViewHolder holder, int position, List payloads) { - holder.onBind(grade, subjectAlertImage); - } - - static class SubItemViewHolder extends FlexibleViewHolder { - - @BindView(R.id.grade_subitem_value) - TextView value; - - @BindView(R.id.grade_subitem_description) - TextView description; - - @BindView(R.id.grade_subitem_date) - TextView date; - - @BindView(R.id.grade_subitem_weight) - TextView weight; - - @BindView(R.id.grade_subitem_alert_image) - View alert; - - private View subjectAlertImage; - - private Context context; - - private Grade item; - - SubItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - context = view.getContext(); - view.setOnClickListener(this); - } - - void onBind(Grade item, View subjectAlertImage) { - this.item = item; - this.subjectAlertImage = subjectAlertImage; - - value.setText(item.getValue()); - value.setBackgroundResource(GradeUtils.getValueColor(item.getValue())); - description.setText(getDescriptionString()); - date.setText(item.getDate()); - weight.setText(String.format("%s: %s", context.getResources().getString(R.string.grade_weight_text), item.getWeight())); - alert.setVisibility(item.getRead() ? View.INVISIBLE : View.VISIBLE); - - if (!item.getRead()) { - numberOfNotReadGrade++; - } - } - - @Override - public void onClick(View view) { - super.onClick(view); - showDialog(); - - if (!item.getRead()) { - numberOfNotReadGrade--; - - if (numberOfNotReadGrade == 0) { - subjectAlertImage.setVisibility(View.INVISIBLE); - } - item.setIsNew(false); - item.setRead(true); - item.update(); - alert.setVisibility(View.INVISIBLE); - } - } - - private String getDescriptionString() { - if (item.getDescription() == null || "".equals(item.getDescription())) { - if (!"".equals(item.getSymbol())) { - return item.getSymbol(); - } else { - return context.getString(R.string.noDescription_text); - } - } else { - return item.getDescription(); - } - } - - private void showDialog() { - GradesDialogFragment dialogFragment = GradesDialogFragment.newInstance(item); - dialogFragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0); - dialogFragment.show(((FragmentActivity) context).getSupportFragmentManager(), item.toString()); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummaryHeader.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummaryHeader.java deleted file mode 100644 index a06fa0d5..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummaryHeader.java +++ /dev/null @@ -1,87 +0,0 @@ -package io.github.wulkanowy.ui.main.grades; - -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 java.util.Locale; - -import butterknife.BindView; -import butterknife.ButterKnife; -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 io.github.wulkanowy.data.db.dao.entities.Subject; - -class GradesSummaryHeader extends AbstractHeaderItem { - - private Subject subject; - - private String average; - - GradesSummaryHeader(Subject subject, float average) { - this.subject = subject; - this.average = String.format(Locale.FRANCE, "%.2f", average); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - GradesSummaryHeader that = (GradesSummaryHeader) o; - - return new EqualsBuilder() - .append(subject, that.subject) - .append(average, that.average) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(subject) - .append(average) - .toHashCode(); - } - - @Override - public int getLayoutRes() { - return R.layout.grades_summary_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, average); - } - - static class HeaderViewHolder extends FlexibleViewHolder { - - @BindView(R.id.grades_summary_header_name) - TextView name; - - @BindView(R.id.grades_summary_header_average) - TextView average; - - HeaderViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - } - - void onBind(Subject item, String value) { - name.setText(item.getName()); - average.setText("-1,00".equals(value) ? "" : value); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummarySubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummarySubItem.java deleted file mode 100644 index 58f418aa..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummarySubItem.java +++ /dev/null @@ -1,84 +0,0 @@ -package io.github.wulkanowy.ui.main.grades; - -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.flexibleadapter.items.IFlexible; -import eu.davidea.viewholders.FlexibleViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.Subject; -import io.github.wulkanowy.utils.GradeUtils; - -public class GradesSummarySubItem - extends AbstractSectionableItem { - - private Subject subject; - - public GradesSummarySubItem(GradesSummaryHeader header, Subject subject) { - super(header); - this.subject = subject; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - GradesSummarySubItem that = (GradesSummarySubItem) 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.grades_summary_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(subject); - } - - static class SubItemViewHolder extends FlexibleViewHolder { - - @BindView(R.id.grades_summary_subitem_final_grade) - TextView finalGrade; - - @BindView(R.id.grades_summary_subitem_predicted_grade) - TextView predictedGrade; - - SubItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - } - - void onBind(Subject item) { - predictedGrade.setText(GradeUtils.getShortGradeValue(item.getPredictedRating())); - finalGrade.setText(GradeUtils.getShortGradeValue(item.getFinalRating())); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/settings/SettingsFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/settings/SettingsFragment.java deleted file mode 100644 index 0f2009be..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/settings/SettingsFragment.java +++ /dev/null @@ -1,181 +0,0 @@ -package io.github.wulkanowy.ui.main.settings; - -import android.content.Intent; -import android.content.SharedPreferences; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v7.app.AppCompatDelegate; -import android.support.v7.preference.Preference; -import android.support.v7.preference.PreferenceFragmentCompat; -import android.widget.Toast; - -import com.google.android.gms.oss.licenses.OssLicensesMenuActivity; - -import io.github.wulkanowy.BuildConfig; -import io.github.wulkanowy.R; -import io.github.wulkanowy.services.jobs.SyncJob; -import io.github.wulkanowy.ui.main.MainActivity; -import io.github.wulkanowy.utils.AppConstant; - -import static io.github.wulkanowy.utils.TimeUtilsKt.isHolidays; - -public class SettingsFragment extends PreferenceFragmentCompat - implements SharedPreferences.OnSharedPreferenceChangeListener { - - public static final String SHARED_KEY_START_TAB = "startup_tab"; - - public static final String SHARED_KEY_GRADES_SUMMARY = "grades_summary"; - - public static final String SHARED_KEY_ATTENDANCE_PRESENT = "attendance_present"; - - public static final String SHARED_KEY_THEME = "theme"; - - public static final String SHARED_KEY_SERVICES_ENABLE = "services_enable"; - - public static final String SHARED_KEY_NOTIFY_ENABLE = "notify_enable"; - - public static final String SHARED_KEY_SERVICES_INTERVAL = "services_interval"; - - public static final String SHARED_KEY_SERVICES_MOBILE_DISABLED = "services_disable_mobile"; - - public static final String SHARED_KEY_ABOUT_VERSION = "about_version"; - - public static final String SHARED_KEY_ABOUT_LICENSES = "about_osl"; - - public static final String SHARED_KEY_ABOUT_REPO = "about_repo"; - - private boolean isStarted; - - private boolean isVisible; - - private Preference.OnPreferenceClickListener onProgrammerListener = new Preference.OnPreferenceClickListener() { - private int clicks = 0; - - @Override - public boolean onPreferenceClick(Preference preference) { - Toast.makeText(getActivity(), getVersionToast(clicks++), Toast.LENGTH_SHORT).show(); - return true; - } - - private int getVersionToast(int click) { - if (0 == click) { - return R.string.about_programmer_step1; - } - - if (1 == click) { - return R.string.about_programmer_step2; - } - - if (9 > click) { - return R.string.about_programmer_step3; - } - - return R.string.about_programmer; - } - }; - - @Override - public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - addPreferencesFromResource(R.xml.preferences); - } - - @Override - public void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - findPreference(SHARED_KEY_ABOUT_VERSION).setSummary(BuildConfig.VERSION_NAME); - findPreference(SHARED_KEY_ABOUT_VERSION).setOnPreferenceClickListener(onProgrammerListener); - findPreference(SHARED_KEY_ABOUT_REPO).setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(AppConstant.REPO_URL))); - findPreference(SHARED_KEY_ABOUT_LICENSES).setIntent(new Intent(getActivity(), OssLicensesMenuActivity.class) - .putExtra("title", R.string.pref_about_osl)); - - if (isHolidays()) { - Preference services = findPreference(SHARED_KEY_SERVICES_ENABLE); - services.setSummary(R.string.pref_services_suspended_on_holidays); - services.setEnabled(false); - } - } - - @Override - public Fragment getCallbackFragment() { - return this; - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (key.equals(SHARED_KEY_SERVICES_ENABLE) || key.equals(SHARED_KEY_SERVICES_INTERVAL) - || key.equals(SHARED_KEY_SERVICES_MOBILE_DISABLED)) { - launchServices(sharedPreferences.getBoolean(SHARED_KEY_SERVICES_ENABLE, true), - sharedPreferences); - } - - if (key.equals(SHARED_KEY_THEME)) { - setCurrentTheme(sharedPreferences); - } - } - - private void setCurrentTheme(SharedPreferences sharedPreferences) { - AppCompatDelegate.setDefaultNightMode(Integer.parseInt(sharedPreferences.getString(SHARED_KEY_THEME, "1"))); - getActivity().finish(); - startActivity(MainActivity - .getStartIntent(getContext()) - .putExtra(MainActivity.EXTRA_CARD_ID_KEY, 4) - ); - getActivity().overridePendingTransition(0, 0); - } - - private void launchServices(boolean start, SharedPreferences sharedPref) { - if (start) { - int newInterval = Integer.parseInt(sharedPref.getString(SHARED_KEY_SERVICES_INTERVAL, "60")); - boolean useOnlyWifi = sharedPref.getBoolean(SHARED_KEY_SERVICES_MOBILE_DISABLED, false); - - SyncJob.stop(getContext()); - SyncJob.start(getContext(), newInterval, useOnlyWifi); - } else { - SyncJob.stop(getContext()); - } - } - - private void setTitle() { - getActivity().setTitle(R.string.settings_text); - } - - @Override - public void setUserVisibleHint(boolean isVisibleToUser) { - super.setUserVisibleHint(isVisibleToUser); - isVisible = isVisibleToUser; - if (isVisible && isStarted) { - setTitle(); - } - } - - @Override - public void onStart() { - super.onStart(); - isStarted = true; - if (isVisible) { - setTitle(); - } - } - - @Override - public void onResume() { - super.onResume(); - getPreferenceScreen().getSharedPreferences() - .registerOnSharedPreferenceChangeListener(this); - } - - @Override - public void onPause() { - super.onPause(); - getPreferenceScreen().getSharedPreferences() - .unregisterOnSharedPreferenceChangeListener(this); - } - - @Override - public void onStop() { - super.onStop(); - isStarted = false; - } -} 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 deleted file mode 100644 index 8344ad76..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - -import android.support.annotation.NonNull; - -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(String date); - - void setAdapterWithTabLayout(); - - boolean isMenuVisible(); - - void setThemeForTab(int position); - } - - interface Presenter extends BaseContract.Presenter { - - void onFragmentActivated(boolean isVisible); - - void attachView(@NonNull View view, OnFragmentIsReadyListener listener); - - void setRestoredPosition(int position); - } -} 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 deleted file mode 100644 index cc668dc2..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java +++ /dev/null @@ -1,127 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.DialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import org.apache.commons.lang3.StringUtils; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; - -public class TimetableDialogFragment extends DialogFragment { - - private static final String ARGUMENT_KEY = "Item"; - - private TimetableLesson lesson; - - @BindView(R.id.timetable_dialog_lesson_value) - TextView lessonName; - - @BindView(R.id.timetable_dialog_teacher_value) - TextView teacher; - - @BindView(R.id.timetable_dialog_group_value) - TextView group; - - @BindView(R.id.timetable_dialog_room_value) - TextView room; - - @BindView(R.id.timetable_dialog_time_value) - TextView time; - - @BindView(R.id.timetable_dialog_description_value) - TextView description; - - @BindView(R.id.timetable_dialog_description) - View descriptionLabel; - - @BindView(R.id.timetable_dialog_teacher) - View teacherLabel; - - @BindView(R.id.timetable_dialog_group) - View groupLabel; - - public TimetableDialogFragment() { - //empty constructor for fragment - } - - public static TimetableDialogFragment newInstance(TimetableLesson lesson) { - TimetableDialogFragment dialogFragment = new TimetableDialogFragment(); - - Bundle bundle = new Bundle(); - bundle.putSerializable(ARGUMENT_KEY, lesson); - - dialogFragment.setArguments(bundle); - - return dialogFragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null) { - lesson = (TimetableLesson) getArguments().getSerializable(ARGUMENT_KEY); - } - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.timetable_dialog, container, false); - - ButterKnife.bind(this, view); - - if (!lesson.getSubject().isEmpty()) { - lessonName.setText(lesson.getSubject()); - } - - if (!lesson.getTeacher().isEmpty()) { - teacher.setText(lesson.getTeacher()); - } else { - teacher.setVisibility(View.GONE); - teacherLabel.setVisibility(View.GONE); - } - - if (!lesson.getGroup().isEmpty()) { - group.setText(lesson.getGroup()); - } 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 deleted file mode 100644 index db3cc83c..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java +++ /dev/null @@ -1,101 +0,0 @@ -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.TabLayout; -import android.support.v4.view.ViewPager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import javax.inject.Inject; -import javax.inject.Named; - -import butterknife.BindView; -import io.github.wulkanowy.R; -import io.github.wulkanowy.ui.base.BaseFragment; -import io.github.wulkanowy.ui.base.BasePagerAdapter; -import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener; -import io.github.wulkanowy.ui.main.timetable.tab.TimetableTabFragment; - -public class TimetableFragment extends BaseFragment implements TimetableContract.View { - - private static final String CURRENT_ITEM_KEY = "CurrentItem"; - - @BindView(R.id.timetable_fragment_viewpager) - ViewPager viewPager; - - @BindView(R.id.timetable_fragment_tab_layout) - TabLayout tabLayout; - - @Named("Timetable") - @Inject - BasePagerAdapter 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); - injectViews(view); - - presenter.attachView(this, (OnFragmentIsReadyListener) getActivity()); - - if (savedInstanceState != null) { - presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY)); - } - return view; - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - if (presenter != null) { - presenter.onFragmentActivated(menuVisible); - } - } - - @Override - public void setTabDataToAdapter(String date) { - pagerAdapter.addFragment(TimetableTabFragment.newInstance(date), date); - } - - @Override - public void setAdapterWithTabLayout() { - viewPager.setAdapter(pagerAdapter); - tabLayout.setupWithViewPager(viewPager); - } - - @Override - public void scrollViewPagerToPosition(int position) { - viewPager.setCurrentItem(position, false); - } - - @Override - public void setThemeForTab(int position) { - TabLayout.Tab tab = tabLayout.getTabAt(position); - if (tab != null) { - tab.setCustomView(R.layout.current_week_tab); - } - } - - @Override - public void setActivityTitle() { - setTitle(getString(R.string.timetable_text)); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem()); - super.onSaveInstanceState(outState); - } - - @Override - public void onDestroyView() { - presenter.detachView(); - super.onDestroyView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableModule.java deleted file mode 100644 index 9ad1ee60..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableModule.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - -import javax.inject.Named; - -import dagger.Binds; -import dagger.Module; -import dagger.Provides; -import dagger.android.ContributesAndroidInjector; -import io.github.wulkanowy.di.scopes.PerChildFragment; -import io.github.wulkanowy.di.scopes.PerFragment; -import io.github.wulkanowy.ui.base.BasePagerAdapter; -import io.github.wulkanowy.ui.main.timetable.tab.TimetableTabFragment; -import io.github.wulkanowy.ui.main.timetable.tab.TimetableTabModule; - -@Module -public abstract class TimetableModule { - - @Named("Timetable") - @PerFragment - @Provides - static BasePagerAdapter providePagerAdapter(TimetableFragment fragment) { - return new BasePagerAdapter(fragment.getChildFragmentManager()); - } - - @PerFragment - @Binds - abstract TimetableContract.Presenter provideTimetablePresenter(TimetablePresenter timetablePresenter); - - @PerChildFragment - @ContributesAndroidInjector(modules = TimetableTabModule.class) - abstract TimetableTabFragment bindTimetableTabFragment(); -} 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 deleted file mode 100644 index 1e9cf0b8..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java +++ /dev/null @@ -1,107 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable; - -import android.support.annotation.NonNull; - -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.async.AbstractTask; -import io.github.wulkanowy.utils.async.AsyncListeners; - -import static io.github.wulkanowy.utils.TimeUtilsKt.getFirstDayOfCurrentWeek; -import static io.github.wulkanowy.utils.TimeUtilsKt.getMondaysFromCurrentSchoolYear; - -public class TimetablePresenter extends BasePresenter - implements TimetableContract.Presenter, AsyncListeners.OnFirstLoadingListener { - - private AbstractTask loadingTask; - - private List dates = new ArrayList<>(); - - private OnFragmentIsReadyListener listener; - - private int positionToScroll = 0; - - private boolean isFirstSight = false; - - @Inject - TimetablePresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void attachView(@NonNull TimetableContract.View view, OnFragmentIsReadyListener listener) { - super.attachView(view); - this.listener = listener; - - if (getView().isMenuVisible()) { - getView().setActivityTitle(); - } - - if (dates.isEmpty()) { - dates = getMondaysFromCurrentSchoolYear(); - } - - if (positionToScroll == 0) { - positionToScroll = dates.indexOf(getFirstDayOfCurrentWeek()); - } - - if (!isFirstSight) { - isFirstSight = true; - - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - } - } - - @Override - public void onFragmentActivated(boolean isVisible) { - if (isVisible) { - getView().setActivityTitle(); - } - } - - @Override - public void onDoInBackgroundLoading() { - for (String date : dates) { - getView().setTabDataToAdapter(date); - } - } - - @Override - public void onCanceledLoadingAsync() { - //do nothing - } - - @Override - public void onEndLoadingAsync(boolean result, Exception exception) { - if (result) { - getView().setAdapterWithTabLayout(); - getView().setThemeForTab(positionToScroll); - getView().scrollViewPagerToPosition(positionToScroll); - listener.onFragmentIsReady(); - } - } - - @Override - public void setRestoredPosition(int position) { - this.positionToScroll = position; - } - - @Override - public void detachView() { - isFirstSight = false; - - if (loadingTask != null) { - loadingTask.cancel(true); - loadingTask = null; - } - super.detachView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableHeader.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableHeader.java deleted file mode 100644 index cb877387..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableHeader.java +++ /dev/null @@ -1,136 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable.tab; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -import java.util.List; - -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem; -import eu.davidea.flexibleadapter.items.IFlexible; -import eu.davidea.viewholders.ExpandableViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.Day; -import io.github.wulkanowy.utils.CommonUtils; - -public class TimetableHeader - extends AbstractExpandableHeaderItem { - - private Day day; - - TimetableHeader(Day day) { - this.day = day; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - TimetableHeader that = (TimetableHeader) o; - - return new EqualsBuilder() - .append(day, that.day) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(day) - .toHashCode(); - } - - @Override - public int getLayoutRes() { - return R.layout.timetable_header; - } - - @Override - public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new HeaderViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, - int position, List payloads) { - holder.onBind(day, getSubItems()); - } - - static class HeaderViewHolder extends ExpandableViewHolder { - - @BindView(R.id.timetable_header_day) - TextView dayName; - - @BindView(R.id.timetable_header_date) - TextView date; - - @BindView(R.id.timetable_header_alert_image) - ImageView alert; - - @BindView(R.id.timetable_header_free_name) - TextView freeName; - - private Context context; - - HeaderViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - view.setOnClickListener(this); - ButterKnife.bind(this, view); - context = view.getContext(); - } - - void onBind(Day item, List subItems) { - dayName.setText(StringUtils.capitalize(item.getDayName())); - date.setText(item.getDate()); - alert.setVisibility(isSubItemNewMovedInOrChanged(subItems) ? View.VISIBLE : View.INVISIBLE); - freeName.setVisibility(item.getFreeDay() ? View.VISIBLE : View.INVISIBLE); - freeName.setText(item.getFreeDayName()); - setInactiveHeader(item.getFreeDay()); - } - - private void setInactiveHeader(boolean inactive) { - ((FrameLayout) getContentView()).setForeground(inactive ? null : getSelectableDrawable()); - dayName.setTextColor(CommonUtils.getThemeAttrColor(context, - inactive ? android.R.attr.textColorSecondary : android.R.attr.textColorPrimary)); - - if (inactive) { - getContentView().setBackgroundColor(CommonUtils.getThemeAttrColor(context, R.attr.colorControlHighlight)); - } else { - getContentView().setBackgroundDrawable(context.getResources().getDrawable(R.drawable.ic_border)); - } - } - - private Drawable getSelectableDrawable() { - int[] attrs = new int[]{R.attr.selectableItemBackground}; - TypedArray typedArray = context.obtainStyledAttributes(attrs); - Drawable drawable = typedArray.getDrawable(0); - typedArray.recycle(); - return drawable; - } - - private boolean isSubItemNewMovedInOrChanged(List subItems) { - boolean isAlertActive = false; - - for (TimetableSubItem subItem : subItems) { - if (subItem.getLesson().getMovedOrCanceled() || - subItem.getLesson().getNewMovedInOrChanged()) { - isAlertActive = true; - } - } - return isAlertActive; - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableSubItem.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableSubItem.java deleted file mode 100644 index 2bf47b33..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableSubItem.java +++ /dev/null @@ -1,144 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable.tab; - -import android.content.Context; -import android.graphics.Paint; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.FragmentActivity; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -import java.util.List; - -import butterknife.BindView; -import butterknife.ButterKnife; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.items.AbstractSectionableItem; -import eu.davidea.flexibleadapter.items.IFlexible; -import eu.davidea.viewholders.FlexibleViewHolder; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; -import io.github.wulkanowy.ui.main.timetable.TimetableDialogFragment; - - -public class TimetableSubItem - extends AbstractSectionableItem { - - private TimetableLesson lesson; - - TimetableSubItem(TimetableHeader header, TimetableLesson lesson) { - super(header); - this.lesson = lesson; - } - - public TimetableLesson getLesson() { - return lesson; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - TimetableSubItem that = (TimetableSubItem) o; - - return new EqualsBuilder() - .append(lesson, that.lesson) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(17, 37) - .append(lesson) - .toHashCode(); - } - - @Override - public int getLayoutRes() { - return R.layout.timetable_subitem; - } - - @Override - public SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new SubItemViewHolder(view, adapter); - } - - @Override - public void bindViewHolder(FlexibleAdapter adapter, SubItemViewHolder holder, - int position, List payloads) { - holder.onBind(lesson); - } - - static class SubItemViewHolder extends FlexibleViewHolder { - - @BindView(R.id.timetable_subItem_lesson) - TextView lessonName; - - @BindView(R.id.timetable_subItem_number_of_lesson) - TextView numberOfLesson; - - @BindView(R.id.timetable_subItem_time) - TextView lessonTime; - - @BindView(R.id.timetable_subItem_room) - TextView room; - - @BindView(R.id.timetable_subItem_alert_image) - ImageView alert; - - private Context context; - - private TimetableLesson item; - - SubItemViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - ButterKnife.bind(this, view); - context = view.getContext(); - view.setOnClickListener(this); - } - - void onBind(TimetableLesson lesson) { - item = lesson; - - lessonName.setText(lesson.getSubject()); - lessonTime.setText(getLessonTimeString()); - numberOfLesson.setText(String.valueOf(lesson.getNumber())); - room.setText(getRoomString()); - alert.setVisibility(lesson.getMovedOrCanceled() || lesson.getNewMovedInOrChanged() - ? View.VISIBLE : View.INVISIBLE); - lessonName.setPaintFlags(lesson.getMovedOrCanceled() ? 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/tab/TimetableTabContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabContract.java deleted file mode 100644 index 0814ff3c..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabContract.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable.tab; - -import java.util.List; - -import io.github.wulkanowy.ui.base.BaseContract; - -public interface TimetableTabContract { - - interface View extends BaseContract.View { - - void updateAdapterList(List headerItems); - - void expandItem(int item); - - void onRefreshSuccess(); - - void hideRefreshingBar(); - - void showNoItem(boolean show); - - void showProgressBar(boolean show); - - void setFreeWeekName(String text); - } - - interface Presenter extends BaseContract.Presenter { - - void onFragmentActivated(boolean isSelected); - - void setArgumentDate(String date); - - void onRefresh(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabFragment.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabFragment.java deleted file mode 100644 index 87db5195..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabFragment.java +++ /dev/null @@ -1,143 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable.tab; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import java.util.List; - -import javax.inject.Inject; - -import butterknife.BindView; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager; -import io.github.wulkanowy.R; -import io.github.wulkanowy.ui.base.BaseFragment; - -public class TimetableTabFragment extends BaseFragment implements TimetableTabContract.View, - SwipeRefreshLayout.OnRefreshListener { - - private static final String ARGUMENT_KEY = "date"; - - @BindView(R.id.timetable_tab_fragment_recycler) - RecyclerView recyclerView; - - @BindView(R.id.timetable_tab_fragment_swipe_refresh) - SwipeRefreshLayout refreshLayout; - - @BindView(R.id.timetable_tab_fragment_progress_bar) - View progressBar; - - @BindView(R.id.timetable_tab_fragment_no_item_container) - View noItemView; - - @BindView(R.id.timetable_tab_fragment_no_item_name) - TextView noItemName; - - @Inject - TimetableTabContract.Presenter presenter; - - @Inject - FlexibleAdapter adapter; - - private boolean isFragmentVisible = false; - - 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); - injectViews(view); - - if (getArguments() != null) { - presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY)); - } - presenter.attachView(this); - presenter.onFragmentActivated(isFragmentVisible); - return view; - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - adapter.setAutoCollapseOnExpand(true); - adapter.setAutoScrollOnExpand(true); - adapter.expandItemsAtStartUp(); - - recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(view.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 expandItem(int position) { - adapter.expand(adapter.getItem(position), true); - recyclerView.scrollToPosition(position); - } - - @Override - public void setMenuVisibility(boolean menuVisible) { - super.setMenuVisibility(menuVisible); - isFragmentVisible = menuVisible; - if (presenter != null) { - presenter.onFragmentActivated(menuVisible); - } - } - - @Override - public void setFreeWeekName(String text) { - noItemName.setText(text); - } - - @Override - public void onRefresh() { - presenter.onRefresh(); - } - - @Override - public void onRefreshSuccess() { - showMessage(R.string.sync_completed); - } - - @Override - public void hideRefreshingBar() { - refreshLayout.setRefreshing(false); - } - - @Override - public void showProgressBar(boolean show) { - progressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE); - } - - @Override - public void showNoItem(boolean show) { - noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); - } - - @Override - public void onDestroyView() { - presenter.detachView(); - super.onDestroyView(); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabModule.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabModule.java deleted file mode 100644 index 1afb055f..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabModule.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable.tab; - -import dagger.Binds; -import dagger.Module; -import dagger.Provides; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import io.github.wulkanowy.di.scopes.PerChildFragment; - -@Module -public abstract class TimetableTabModule { - - @PerChildFragment - @Binds - abstract TimetableTabContract.Presenter provideTimetableTabPresneter(TimetableTabPresenter timetableTabPresenter); - - @PerChildFragment - @Provides - static FlexibleAdapter provideTimetableAdapter() { - return new FlexibleAdapter<>(null); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabPresenter.java b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabPresenter.java deleted file mode 100644 index f30dd804..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabPresenter.java +++ /dev/null @@ -1,200 +0,0 @@ -package io.github.wulkanowy.ui.main.timetable.tab; - -import android.support.annotation.NonNull; - -import org.threeten.bp.LocalDate; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.data.db.dao.entities.Day; -import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; -import io.github.wulkanowy.data.db.dao.entities.Week; -import io.github.wulkanowy.ui.base.BasePresenter; -import io.github.wulkanowy.utils.AppConstant; -import io.github.wulkanowy.utils.FabricUtils; -import io.github.wulkanowy.utils.async.AbstractTask; -import io.github.wulkanowy.utils.async.AsyncListeners; - -import static io.github.wulkanowy.utils.TimeUtilsKt.getParsedDate; -import static io.github.wulkanowy.utils.TimeUtilsKt.isDateInWeek; - -public class TimetableTabPresenter extends BasePresenter - implements TimetableTabContract.Presenter, AsyncListeners.OnRefreshListener, - AsyncListeners.OnFirstLoadingListener { - - private AbstractTask refreshTask; - - private AbstractTask loadingTask; - - private List headerItems = new ArrayList<>(); - - private String date; - - private String freeWeekName; - - private boolean isFirstSight = false; - - @Inject - TimetableTabPresenter(RepositoryContract repository) { - super(repository); - } - - @Override - public void attachView(@NonNull TimetableTabContract.View view) { - super.attachView(view); - getView().showProgressBar(true); - getView().showNoItem(false); - } - - @Override - public void onFragmentActivated(boolean isSelected) { - if (!isFirstSight && isSelected && isViewAttached()) { - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - } else if (!isSelected) { - cancelAsyncTasks(); - } - } - - @Override - public void onRefresh() { - if (getView().isNetworkConnected()) { - refreshTask = new AbstractTask(); - refreshTask.setOnRefreshListener(this); - refreshTask.execute(); - } else { - getView().showNoNetworkMessage(); - getView().hideRefreshingBar(); - } - } - - @Override - public void onDoInBackgroundRefresh() throws Exception { - syncData(); - } - - @Override - public void onCanceledRefreshAsync() { - if (isViewAttached()) { - getView().hideRefreshingBar(); - } - } - - @Override - public void onEndRefreshAsync(boolean result, Exception exception) { - if (result) { - loadingTask = new AbstractTask(); - loadingTask.setOnFirstLoadingListener(this); - loadingTask.execute(); - - getView().onRefreshSuccess(); - } else { - getView().showMessage(getRepository().getResRepo().getErrorLoginMessage(exception)); - } - getView().hideRefreshingBar(); - - FabricUtils.logRefresh("Timetable", result, date); - } - - @Override - public void onDoInBackgroundLoading() throws Exception { - Week week = getRepository().getDbRepo().getWeek(date); - - if (week == null || !week.getTimetableSynced()) { - syncData(); - week = getRepository().getDbRepo().getWeek(date); - } - - week.resetDayList(); - List dayList = week.getDayList(); - - headerItems = new ArrayList<>(); - - boolean isFreeWeek = true; - - for (Day day : dayList) { - day.resetTimetableLessons(); - TimetableHeader headerItem = new TimetableHeader(day); - - if (isFreeWeek) { - isFreeWeek = day.getFreeDay(); - } - - List lessonList = day.getTimetableLessons(); - - List subItems = new ArrayList<>(); - - for (TimetableLesson lesson : lessonList) { - subItems.add(new TimetableSubItem(headerItem, lesson)); - } - - headerItem.setSubItems(subItems); - headerItem.setExpanded(false); - headerItems.add(headerItem); - } - - if (isFreeWeek) { - freeWeekName = dayList.get(4).getFreeDayName(); - headerItems = new ArrayList<>(); - } - } - - @Override - public void onCanceledLoadingAsync() { - // do nothing - } - - @Override - public void onEndLoadingAsync(boolean result, Exception exception) { - getView().showNoItem(headerItems.isEmpty()); - getView().updateAdapterList(headerItems); - - if (headerItems.isEmpty()) { - getView().setFreeWeekName(freeWeekName); - } else { - expandCurrentDayHeader(); - } - getView().showProgressBar(false); - isFirstSight = true; - } - - private void expandCurrentDayHeader() { - LocalDate monday = getParsedDate(date, AppConstant.DATE_PATTERN); - - if (isDateInWeek(monday, LocalDate.now()) && !isFirstSight) { - getView().expandItem(LocalDate.now().getDayOfWeek().getValue() - 1); - } - } - - @Override - public void setArgumentDate(String date) { - this.date = date; - } - - private void syncData() throws Exception { - getRepository().getSyncRepo().syncTimetable(0, date); - } - - private void cancelAsyncTasks() { - if (refreshTask != null) { - refreshTask.cancel(true); - refreshTask = null; - } - if (loadingTask != null) { - loadingTask.cancel(true); - loadingTask = null; - } - } - - @Override - public void detachView() { - isFirstSight = false; - cancelAsyncTasks(); - super.detachView(); - } -} 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 new file mode 100644 index 00000000..8d07566f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt @@ -0,0 +1,94 @@ +package io.github.wulkanowy.ui.modules.about + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.mikepenz.aboutlibraries.LibsBuilder +import com.mikepenz.aboutlibraries.LibsFragmentCompat +import io.github.wulkanowy.BuildConfig +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.openInternetBrowser +import io.github.wulkanowy.utils.withOnExtraListener +import javax.inject.Inject + +class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { + + @Inject + lateinit var presenter: AboutPresenter + + @Inject + lateinit var fragmentCompat: LibsFragmentCompat + + companion object { + fun newInstance() = AboutFragment() + } + + override val titleStringId: Int + get() = R.string.about_title + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + presenter.onAttachView(this) + return Bundle().apply { + putSerializable("data", LibsBuilder() + .withAboutAppName(getString(R.string.app_name)) + .withAboutVersionShown(true) + .withAboutIconShown(true) + .withLicenseShown(true) + .withAboutSpecial1(getString(R.string.about_discord_invite)) + .withAboutSpecial2(getString(R.string.about_homepage)) + .withAboutSpecial3(getString(R.string.about_feedback)) + .withFields(R.string::class.java.fields) + .withCheckCachedDetection(false) + .withExcludedLibraries("fastadapter", "AndroidIconics", "Jsoup", "Retrofit", "okio", + "Butterknife", "CircleImageView") + .withOnExtraListener { presenter.onExtraSelect(it) }) + }.let { + fragmentCompat.onCreateView(inflater.context, inflater, container, savedInstanceState, it) + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + fragmentCompat.onViewCreated(view, savedInstanceState) + } + + override fun openDiscordInviteView() { + context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage) + } + + override fun openHomepageWebView() { + context?.openInternetBrowser("https://wulkanowy.github.io/", ::showMessage) + } + + override fun openEmailClientView() { + val intent = Intent(Intent.ACTION_SENDTO).apply { + data = Uri.parse("mailto:") + putExtra(Intent.EXTRA_EMAIL, Array(1) { "wulkanowyinc@gmail.com" }) + putExtra(Intent.EXTRA_SUBJECT, "Zgłoszenie błędu") + putExtra(Intent.EXTRA_TEXT, "Tu umieść treść zgłoszenia\n\n" + "-".repeat(40) + "\n" + """ + Build: ${BuildConfig.VERSION_CODE} + SDK: ${android.os.Build.VERSION.SDK_INT} + Device: ${android.os.Build.MANUFACTURER} ${android.os.Build.MODEL} + """.trimIndent()) + } + + context?.let { + if (intent.resolveActivity(it.packageManager) != null) { + startActivity(Intent.createChooser(intent, getString(R.string.about_feedback))) + } else { + it.openInternetBrowser("https://github.com/wulkanowy/wulkanowy/issues", ::showMessage) + } + } + } + + 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 new file mode 100644 index 00000000..cc5ba7cf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutModule.kt @@ -0,0 +1,14 @@ +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 new file mode 100644 index 00000000..736bf0ce --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt @@ -0,0 +1,48 @@ +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.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject + +class AboutPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: AboutView) { + super.onAttachView(view) + Timber.i("About view was initialized") + } + + fun onExtraSelect(type: Libs.SpecialButton?) { + view?.run { + when (type) { + SPECIAL1 -> { + Timber.i("Opening discord invide page") + analytics.logEvent("open_page", "name" to "discord") + openDiscordInviteView() + } + SPECIAL2 -> { + Timber.i("Opening home page") + analytics.logEvent("open_page", "name" to "home") + openHomepageWebView() + } + SPECIAL3 -> { + Timber.i("Opening email client") + analytics.logEvent("open_page", "name" to "email") + openEmailClientView() + } + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt new file mode 100644 index 00000000..5b206ad8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.ui.modules.about + +import io.github.wulkanowy.ui.base.BaseView + +interface AboutView : BaseView { + + fun openDiscordInviteView() + + fun openEmailClientView() + + fun openHomepageWebView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt new file mode 100644 index 00000000..f23a1eb5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt @@ -0,0 +1,108 @@ +package io.github.wulkanowy.ui.modules.account + +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.base.BaseActivity +import io.github.wulkanowy.ui.modules.login.LoginActivity +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?.let { + startActivity(LoginActivity.getStartIntent(it)) + } + } + + override fun showExpiredDialog() { + (activity as? BaseActivity<*>)?.showExpiredDialog() + } + + override fun openClearLoginView() { + (activity as? BaseActivity<*>)?.openClearLoginView() + } + + 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 recreateMainView() { + activity?.recreate() + } + + 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 new file mode 100644 index 00000000..972d10a2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.ui.modules.account + +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.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) + } + + @SuppressLint("SetTextI18n") + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { + holder.apply { + accountItemName.text = "${student.studentName} ${student.className}" + accountItemSchool.text = student.schoolName + accountItemImage.setBackgroundResource(if (student.isCurrent) R.drawable.ic_account_circular_border else 0) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as AccountItem + + if (student != other.student) return false + + return true + } + + override fun hashCode(): Int { + var result = student.hashCode() + result = 31 * result + student.id.toInt() + return result + } + + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt new file mode 100644 index 00000000..e9b4b81e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt @@ -0,0 +1,103 @@ +package io.github.wulkanowy.ui.modules.account + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.services.sync.SyncManager +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.SchedulersProvider +import io.reactivex.Single +import timber.log.Timber +import javax.inject.Inject + +class AccountPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val syncManager: SyncManager +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: AccountView) { + super.onAttachView(view) + view.initView() + Timber.i("Account dialog view was initialized") + loadData() + } + + fun onAddSelected() { + Timber.i("Select add account") + view?.openLoginView() + } + + fun onRemoveSelected() { + Timber.i("Select remove account") + view?.showConfirmDialog() + } + + fun onLogoutConfirm() { + Timber.i("Attempt to logout current user ") + disposable.add(studentRepository.getCurrentStudent() + .flatMapCompletable { studentRepository.logoutStudent(it) } + .andThen(studentRepository.getSavedStudents(false)) + .flatMap { + if (it.isNotEmpty()) studentRepository.switchStudent(it[0]).toSingle { it } + else Single.just(it) + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { view?.dismissView() } + .subscribe({ + view?.apply { + if (it.isEmpty()) { + Timber.i("Logout result: Open login view") + syncManager.stopSyncWorker() + openClearLoginView() + } else { + Timber.i("Logout result: Switch to another student") + recreateMainView() + } + } + }, { + Timber.i("Logout result: An exception occurred") + errorHandler.dispatch(it) + })) + } + + fun onItemSelected(item: AbstractFlexibleItem<*>) { + if (item is AccountItem) { + Timber.i("Select student item ${item.student.id}") + if (item.student.isCurrent) { + view?.dismissView() + } else { + Timber.i("Attempt to change a student") + disposable.add(studentRepository.switchStudent(item.student) + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { view?.dismissView() } + .subscribe({ + Timber.i("Change a student result: Success") + view?.recreateMainView() + }, { + Timber.i("Change a student result: An exception occurred") + errorHandler.dispatch(it) + })) + } + } + } + + private fun loadData() { + Timber.i("Loading account data started") + disposable.add(studentRepository.getSavedStudents(false) + .map { it.map { item -> AccountItem(item) } } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + Timber.i("Loading account result: Success") + view?.updateData(it) + }, { + Timber.i("Loading account result: An exception occurred") + errorHandler.dispatch(it) + })) + } +} + diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt new file mode 100644 index 00000000..ede5023b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt @@ -0,0 +1,19 @@ +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 recreateMainView() +} + 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 new file mode 100644 index 00000000..611dd999 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt @@ -0,0 +1,48 @@ +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 new file mode 100644 index 00000000..f3131098 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt @@ -0,0 +1,158 @@ +package io.github.wulkanowy.ui.modules.attendance + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.FlexibleItemDecoration +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.ui.base.BaseFragment +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 : BaseFragment(), AttendanceView, MainView.MainChildView, MainView.TitledView { + + @Inject + lateinit var presenter: AttendancePresenter + + @Inject + lateinit var attendanceAdapter: FlexibleAdapter> + + companion object { + private const val SAVED_DATE_KEY = "CURRENT_DATE" + + fun newInstance() = AttendanceFragment() + } + + override val titleStringId: Int + get() = R.string.attendance_title + + override val isViewEmpty: Boolean + get() = attendanceAdapter.isEmpty + + override val currentStackSize: Int? + get() = (activity as? MainActivity)?.currentStackSize + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_attendance, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + messageContainer = attendanceRecycler + presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) + } + + override fun initView() { + attendanceAdapter.apply { + setOnItemClickListener { presenter.onAttendanceItemSelected(it) } + } + + attendanceRecycler.run { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = attendanceAdapter + addItemDecoration(FlexibleItemDecoration(context) + .withDefaultDivider() + .withDrawDividerOnLastItem(false) + ) + } + attendanceSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + attendancePreviousButton.setOnClickListener { presenter.onPreviousDay() } + attendanceNextButton.setOnClickListener { presenter.onNextDay() } + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.action_menu_attendance, menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected() + else false + } + + override fun updateData(data: List) { + attendanceAdapter.updateDataSet(data, true) + } + + override fun updateNavigationDay(date: String) { + attendanceNavDate.text = date + } + + override fun clearData() { + attendanceAdapter.clear() + } + + override fun resetView() { + attendanceRecycler.smoothScrollToPosition(0) + } + + override fun onFragmentReselected() { + if (::presenter.isInitialized) presenter.onViewReselected() + } + + override fun popView() { + (activity as? MainActivity)?.popView() + } + + override fun showEmpty(show: Boolean) { + attendanceEmpty.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showProgress(show: Boolean) { + attendanceProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun enableSwipe(enable: Boolean) { + attendanceSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + attendanceRecycler.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun hideRefresh() { + attendanceSwipe.isRefreshing = false + } + + override fun showPreButton(show: Boolean) { + attendancePreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showNextButton(show: Boolean) { + attendanceNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showAttendanceDialog(lesson: Attendance) { + (activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson)) + } + + override fun openSummaryView() { + (activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance()) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt new file mode 100644 index 00000000..16a140cb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.ui.modules.attendance + +import android.view.View +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Attendance +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_attendance.* + +class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_attendance + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { + return ViewHolder(view, adapter) + } + + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { + holder.apply { + attendanceItemNumber.text = attendance.number.toString() + attendanceItemSubject.text = attendance.subject + attendanceItemDescription.text = attendance.name + attendanceItemAlert.visibility = attendance.run { if (absence && !excused) VISIBLE else INVISIBLE } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as AttendanceItem + + if (attendance != other.attendance) return false + + return true + } + + override fun hashCode(): Int { + var result = attendance.hashCode() + result = 31 * result + attendance.id.toInt() + return result + } + + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt new file mode 100644 index 00000000..5e7333e1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -0,0 +1,135 @@ +package io.github.wulkanowy.ui.modules.attendance + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.* +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDate.now +import org.threeten.bp.LocalDate.ofEpochDay +import timber.log.Timber +import java.util.concurrent.TimeUnit.MILLISECONDS +import javax.inject.Inject + +class AttendancePresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val attendanceRepository: AttendanceRepository, + private val semesterRepository: SemesterRepository, + private val prefRepository: PreferencesRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + lateinit var currentDate: LocalDate + private set + + fun onAttachView(view: AttendanceView, date: Long?) { + super.onAttachView(view) + view.initView() + Timber.i("Attendance view was initialized") + loadData(ofEpochDay(date ?: now().previousOrSameSchoolDay.toEpochDay())) + reloadView() + } + + fun onPreviousDay() { + loadData(currentDate.previousSchoolDay) + reloadView() + } + + fun onNextDay() { + loadData(currentDate.nextSchoolDay) + reloadView() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the attendance") + loadData(currentDate, true) + } + + fun onViewReselected() { + Timber.i("Attendance view is reselected") + view?.also { view -> + if (view.currentStackSize == 1) { + now().previousOrSameSchoolDay.also { + if (currentDate != it) { + loadData(it) + reloadView() + } else if (!view.isViewEmpty) view.resetView() + } + } else view.popView() + } + } + + fun onAttendanceItemSelected(item: AbstractFlexibleItem<*>?) { + if (item is AttendanceItem) { + Timber.i("Select attendance item ${item.attendance.id}") + view?.showAttendanceDialog(item.attendance) + } + } + + fun onSummarySwitchSelected(): Boolean { + view?.openSummaryView() + return true + } + + private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { + Timber.i("Loading attendance data started") + currentDate = date + disposable.apply { + clear() + add(studentRepository.getCurrentStudent() + .delay(200, MILLISECONDS) + .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { attendanceRepository.getAttendance(it, date, date, forceRefresh) } + .map { list -> + if (prefRepository.isShowPresent) list + else list.filter { !it.presence } + } + .map { items -> items.map { AttendanceItem(it) } } + .map { items -> items.sortedBy { it.attendance.number } } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + } + } + .subscribe({ + Timber.i("Loading attendance result: Success") + view?.apply { + updateData(it) + showEmpty(it.isEmpty()) + showContent(it.isNotEmpty()) + } + analytics.logEvent("load_attendance", "items" to it.size, "force_refresh" to forceRefresh) + }) { + Timber.i("Loading attendance result: An exception occurred") + view?.run { showEmpty(isViewEmpty) } + errorHandler.dispatch(it) + } + ) + } + } + + private fun reloadView() { + Timber.i("Reload attendance view with the date ${currentDate.toFormattedString()}") + view?.apply { + showProgress(true) + enableSwipe(false) + showContent(false) + showEmpty(false) + clearData() + showNextButton(!currentDate.plusDays(1).isHolidays) + showPreButton(!currentDate.minusDays(1).isHolidays) + updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize()) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt new file mode 100644 index 00000000..04fe94a4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt @@ -0,0 +1,41 @@ +package io.github.wulkanowy.ui.modules.attendance + +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.ui.base.BaseView + +interface AttendanceView : BaseView { + + val isViewEmpty: Boolean + + val currentStackSize: Int? + + fun initView() + + fun updateData(data: List) + + fun updateNavigationDay(date: String) + + fun clearData() + + fun hideRefresh() + + fun resetView() + + fun showEmpty(show: Boolean) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showAttendanceDialog(lesson: Attendance) + + fun openSummaryView() + + fun popView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt new file mode 100644 index 00000000..1845bdda --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt @@ -0,0 +1,125 @@ +package io.github.wulkanowy.ui.modules.attendance.summary + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.TextView +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseFragment +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 : BaseFragment(), AttendanceSummaryView, MainView.TitledView { + + @Inject + lateinit var presenter: AttendanceSummaryPresenter + + @Inject + lateinit var attendanceSummaryAdapter: FlexibleAdapter> + + private lateinit var subjectsAdapter: ArrayAdapter + + companion object { + private const val SAVED_SUBJECT_KEY = "CURRENT_SUBJECT" + + fun newInstance() = AttendanceSummaryFragment() + } + + override val titleStringId: Int + get() = R.string.attendance_title + + override val isViewEmpty + get() = attendanceSummaryAdapter.isEmpty + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_attendance_summary, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + messageContainer = attendanceSummaryRecycler + presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SUBJECT_KEY)) + } + + override fun initView() { + attendanceSummaryRecycler.run { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = attendanceSummaryAdapter + } + attendanceSummarySwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + + context?.let { + subjectsAdapter = ArrayAdapter(it, android.R.layout.simple_spinner_item, ArrayList()) + subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject) + } + + attendanceSummarySubjects.run { + adapter = subjectsAdapter + setOnItemSelectedListener { presenter.onSubjectSelected((it as TextView).text.toString()) } + } + } + + override fun updateSubjects(data: ArrayList) { + subjectsAdapter.run { + clear() + addAll(data) + notifyDataSetChanged() + } + } + + override fun updateDataSet(data: List, header: AttendanceSummaryScrollableHeader) { + attendanceSummaryAdapter.apply { + updateDataSet(data, true) + removeAllScrollableHeaders() + addScrollableHeader(header) + } + } + + override fun clearView() { + attendanceSummaryAdapter.clear() + } + + override fun showEmpty(show: Boolean) { + attendanceSummaryEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showProgress(show: Boolean) { + attendanceSummaryProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + attendanceSummarySwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + attendanceSummaryRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun showSubjects(show: Boolean) { + attendanceSummarySubjectsContainer.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun hideRefresh() { + attendanceSummarySwipe.isRefreshing = false + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putInt(SAVED_SUBJECT_KEY, presenter.currentSubjectId) + } + + override fun onDestroyView() { + super.onDestroyView() + presenter.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt new file mode 100644 index 00000000..265d6ce4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt @@ -0,0 +1,80 @@ +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 new file mode 100644 index 00000000..dc6fe5b3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt @@ -0,0 +1,140 @@ +package io.github.wulkanowy.ui.modules.attendance.summary + +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.github.wulkanowy.data.db.entities.Subject +import io.github.wulkanowy.data.repositories.attendancesummary.AttendanceSummaryRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.subject.SubjectRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.calculatePercentage +import io.github.wulkanowy.utils.getFormattedName +import timber.log.Timber +import java.lang.String.format +import java.util.Locale.FRANCE +import java.util.concurrent.TimeUnit.MILLISECONDS +import javax.inject.Inject + +class AttendanceSummaryPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val attendanceSummaryRepository: AttendanceSummaryRepository, + private val subjectRepository: SubjectRepository, + private val semesterRepository: SemesterRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + private var subjects = emptyList() + + var currentSubjectId = -1 + private set + + fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) { + super.onAttachView(view) + view.initView() + Timber.i("Attendance summary view was initialized with subject id ${subjectId ?: -1}") + loadData(subjectId ?: -1) + loadSubjects() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the attendance summary") + loadData(currentSubjectId, true) + } + + fun onSubjectSelected(name: String) { + Timber.i("Select attendance summary subject $name") + view?.run { + showContent(false) + showProgress(true) + enableSwipe(false) + clearView() + } + (subjects.singleOrNull { it.name == name }?.realId ?: -1).let { + if (it != currentSubjectId) loadData(it) + } + } + + private fun loadData(subjectId: Int, forceRefresh: Boolean = false) { + Timber.i("Loading attendance summary data started") + currentSubjectId = subjectId + disposable.apply { + clear() + add(studentRepository.getCurrentStudent() + .delay(200, MILLISECONDS) + .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { attendanceSummaryRepository.getAttendanceSummary(it, subjectId, forceRefresh) } + .map { createAttendanceSummaryItems(it) to AttendanceSummaryScrollableHeader(formatPercentage(it.calculatePercentage())) } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + } + } + .subscribe({ + Timber.i("Loading attendance summary result: Success") + view?.apply { + showEmpty(it.first.isEmpty()) + showContent(it.first.isNotEmpty()) + updateDataSet(it.first, it.second) + } + analytics.logEvent("load_attendance_summary", "items" to it.first.size, "force_refresh" to forceRefresh, "item_id" to subjectId) + }) { + Timber.i("Loading attendance summary result: An exception occurred") + view?.run { showEmpty(isViewEmpty) } + errorHandler.dispatch(it) + } + ) + } + } + + private fun loadSubjects() { + Timber.i("Loading attendance summary subjects started") + disposable.add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { subjectRepository.getSubjects(it) } + .doOnSuccess { subjects = it } + .map { ArrayList(it.map { subject -> subject.name }) } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + Timber.i("Loading attendance summary subjects result: Success") + view?.run { + view?.updateSubjects(it) + showSubjects(true) + } + }, { + Timber.i("Loading attendance summary subjects result: An exception occurred") + errorHandler.dispatch(it) + }) + ) + } + + private fun createAttendanceSummaryItems(attendanceSummary: List): List { + return attendanceSummary.sortedByDescending { it.id }.map { + AttendanceSummaryItem( + month = it.month.getFormattedName(), + percentage = formatPercentage(it.calculatePercentage()), + present = it.presence.toString(), + absence = it.absence.toString(), + excusedAbsence = it.absenceExcused.toString(), + schoolAbsence = it.absenceForSchoolReasons.toString(), + exemption = it.exemption.toString(), + lateness = it.lateness.toString(), + excusedLateness = it.latenessExcused.toString() + ) + } + } + + private fun formatPercentage(percentage: Double): String { + return if (percentage == 0.0) "0%" + else "${format(FRANCE, "%.2f", percentage)}%" + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryScrollableHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryScrollableHeader.kt new file mode 100644 index 00000000..c258f71d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryScrollableHeader.kt @@ -0,0 +1,46 @@ +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 new file mode 100644 index 00000000..50f03e20 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt @@ -0,0 +1,28 @@ +package io.github.wulkanowy.ui.modules.attendance.summary + +import io.github.wulkanowy.ui.base.BaseView + +interface AttendanceSummaryView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun hideRefresh() + + fun showContent(show: Boolean) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showEmpty(show: Boolean) + + fun updateDataSet(data: List, header: AttendanceSummaryScrollableHeader) + + fun updateSubjects(data: ArrayList) + + fun showSubjects(show: Boolean) + + fun clearView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt new file mode 100644 index 00000000..ed5092c9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt @@ -0,0 +1,50 @@ +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 new file mode 100644 index 00000000..47f6d587 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt @@ -0,0 +1,131 @@ +package io.github.wulkanowy.ui.modules.exam + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import android.view.ViewGroup +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.FlexibleItemDecoration +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.fragment_exam.* +import javax.inject.Inject + +class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView.TitledView { + + @Inject + lateinit var presenter: ExamPresenter + + @Inject + lateinit var examAdapter: FlexibleAdapter> + + companion object { + private const val SAVED_DATE_KEY = "CURRENT_DATE" + + fun newInstance() = ExamFragment() + } + + override val titleStringId: Int + get() = R.string.exam_title + + override val isViewEmpty: Boolean + get() = examAdapter.isEmpty + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_exam, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + messageContainer = examRecycler + presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) + } + + override fun initView() { + examAdapter.run { + setOnItemClickListener { presenter.onExamItemSelected(it) } + } + examRecycler.run { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = examAdapter + addItemDecoration(FlexibleItemDecoration(context) + .withDefaultDivider(R.layout.item_exam) + .withDrawDividerOnLastItem(false) + ) + } + examSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + examPreviousButton.setOnClickListener { presenter.onPreviousWeek() } + examNextButton.setOnClickListener { presenter.onNextWeek() } + } + + override fun hideRefresh() { + examSwipe.isRefreshing = false + } + + override fun updateData(data: List) { + examAdapter.updateDataSet(data, true) + } + + override fun updateNavigationWeek(date: String) { + examNavDate.text = date + } + + override fun clearData() { + examAdapter.clear() + } + + override fun resetView() { + examRecycler.scrollToPosition(0) + } + + override fun onFragmentReselected() { + if (::presenter.isInitialized) presenter.onViewReselected() + } + + override fun showEmpty(show: Boolean) { + examEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showProgress(show: Boolean) { + examProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + examSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + examRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun showPreButton(show: Boolean) { + examPreviousButton.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showNextButton(show: Boolean) { + examNextButton.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showExamDialog(exam: Exam) { + (activity as? MainActivity)?.showDialogFragment(ExamDialog.newInstance(exam)) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamHeader.kt new file mode 100644 index 00000000..0a5b862c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamHeader.kt @@ -0,0 +1,52 @@ +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 new file mode 100644 index 00000000..8971b4df --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.ui.modules.exam + +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractSectionableItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Exam +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_exam.* + +class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem(header) { + + override fun getLayoutRes() = R.layout.item_exam + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { + return ViewHolder(view, adapter) + } + + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { + holder.run { + examItemSubject.text = exam.subject + examItemTeacher.text = exam.teacher + examItemType.text = exam.type + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ExamItem + + if (exam != other.exam) return false + + return true + } + + override fun hashCode(): Int { + var result = exam.hashCode() + result = 31 * result + exam.id.toInt() + return result + } + + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt new file mode 100644 index 00000000..4045a956 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt @@ -0,0 +1,135 @@ +package io.github.wulkanowy.ui.modules.exam + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.data.repositories.exam.ExamRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.friday +import io.github.wulkanowy.utils.isHolidays +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import io.github.wulkanowy.utils.toFormattedString +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDate.now +import org.threeten.bp.LocalDate.ofEpochDay +import timber.log.Timber +import java.util.concurrent.TimeUnit.MILLISECONDS +import javax.inject.Inject + +class ExamPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val examRepository: ExamRepository, + private val semesterRepository: SemesterRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + lateinit var currentDate: LocalDate + private set + + fun onAttachView(view: ExamView, date: Long?) { + super.onAttachView(view) + view.initView() + Timber.i("Exam view was initialized") + loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay())) + reloadView() + } + + fun onPreviousWeek() { + loadData(currentDate.minusDays(7)) + reloadView() + } + + fun onNextWeek() { + loadData(currentDate.plusDays(7)) + reloadView() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the exam") + loadData(currentDate, true) + } + + fun onExamItemSelected(item: AbstractFlexibleItem<*>?) { + if (item is ExamItem) { + Timber.i("Select exam item ${item.exam.id}") + view?.showExamDialog(item.exam) + } + } + + fun onViewReselected() { + Timber.i("Exam view is reselected") + now().nextOrSameSchoolDay.also { + if (currentDate != it) { + loadData(it) + reloadView() + } else if (view?.isViewEmpty == false) view?.resetView() + } + } + + private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { + Timber.i("Loading exam data started") + currentDate = date + disposable.apply { + clear() + add(studentRepository.getCurrentStudent() + .delay(200, MILLISECONDS) + .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { + examRepository.getExams(it, currentDate.monday, currentDate.friday, forceRefresh) + }.map { it.groupBy { exam -> exam.date }.toSortedMap() } + .map { createExamItems(it) } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + } + } + .subscribe({ + Timber.i("Loading exam result: Success") + view?.apply { + updateData(it) + showEmpty(it.isEmpty()) + showContent(it.isNotEmpty()) + } + analytics.logEvent("load_exam", "items" to it.size, "force_refresh" to forceRefresh) + }) { + Timber.i("Loading exam result: An exception occurred") + view?.run { showEmpty(isViewEmpty) } + errorHandler.dispatch(it) + }) + } + } + + private fun createExamItems(items: Map>): List { + return items.flatMap { + ExamHeader(it.key).let { header -> + it.value.reversed().map { item -> ExamItem(header, item) } + } + } + } + + private fun reloadView() { + Timber.i("Reload exam view with the date ${currentDate.toFormattedString()}") + view?.apply { + showProgress(true) + enableSwipe(false) + showContent(false) + showEmpty(false) + clearData() + showPreButton(!currentDate.minusDays(7).isHolidays) + showNextButton(!currentDate.plusDays(7).isHolidays) + updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " + + currentDate.friday.toFormattedString("dd.MM")) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt new file mode 100644 index 00000000..888cb05e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt @@ -0,0 +1,35 @@ +package io.github.wulkanowy.ui.modules.exam + +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.ui.base.BaseView + +interface ExamView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun updateNavigationWeek(date: String) + + fun clearData() + + fun hideRefresh() + + fun resetView() + + fun showEmpty(show: Boolean) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showExamDialog(exam: Exam) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/CustomTabLayout.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/CustomTabLayout.kt new file mode 100644 index 00000000..e6f01497 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/CustomTabLayout.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.ui.modules.grade + +import android.content.Context +import android.util.AttributeSet +import android.view.ViewGroup +import com.google.android.material.tabs.TabLayout + +/** + * @see Tabs don't fit to screen with tabmode=scrollable, Even with a Custom Tab Layout + */ +class CustomTabLayout : TabLayout { + + constructor(context: Context) : super(context) + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + setMeasuredDimension(widthMeasureSpec, heightMeasureSpec) + val tabLayout = getChildAt(0) as ViewGroup + val childCount = tabLayout.childCount + + if (childCount == 0) return + + val tabMinWidth = context.resources.displayMetrics.widthPixels / childCount + + for (i in 0 until childCount) { + tabLayout.getChildAt(i).minimumWidth = tabMinWidth + } + + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt new file mode 100644 index 00000000..cff60268 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt @@ -0,0 +1,72 @@ +package io.github.wulkanowy.ui.modules.grade + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.grade.GradeRepository +import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.utils.calcAverage +import io.github.wulkanowy.utils.changeModifier +import io.reactivex.Maybe +import io.reactivex.Single +import javax.inject.Inject + +class GradeAverageProvider @Inject constructor( + private val preferencesRepository: PreferencesRepository, + private val gradeRepository: GradeRepository, + private val gradeSummaryRepository: GradeSummaryRepository +) { + + fun getGradeAverage(student: Student, semesters: List, selectedSemesterId: Int, forceRefresh: Boolean): Single> { + return when (preferencesRepository.gradeAverageMode) { + "all_year" -> getAllYearAverage(student, semesters, selectedSemesterId, forceRefresh) + "only_one_semester" -> getOnlyOneSemesterAverage(student, semesters, selectedSemesterId, forceRefresh) + else -> throw IllegalArgumentException("Incorrect grade average mode: ${preferencesRepository.gradeAverageMode} ") + } + } + + private fun getAllYearAverage(student: Student, semesters: List, semesterId: Int, forceRefresh: Boolean): Single> { + val selectedSemester = semesters.single { it.semesterId == semesterId } + val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 } + val plusModifier = preferencesRepository.gradePlusModifier + val minusModifier = preferencesRepository.gradeMinusModifier + + return getAverageFromGradeSummary(selectedSemester, forceRefresh) + .switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh) + .flatMap { firstGrades -> + if (selectedSemester == firstSemester) Single.just(firstGrades) + else { + gradeRepository.getGrades(student, firstSemester) + .map { secondGrades -> secondGrades + firstGrades } + } + }.map { grades -> + grades.map { it.changeModifier(plusModifier, minusModifier) } + .groupBy { it.subject } + .mapValues { it.value.calcAverage() } + }) + } + + private fun getOnlyOneSemesterAverage(student: Student, semesters: List, semesterId: Int, forceRefresh: Boolean): Single> { + val selectedSemester = semesters.single { it.semesterId == semesterId } + val plusModifier = preferencesRepository.gradePlusModifier + val minusModifier = preferencesRepository.gradeMinusModifier + + return getAverageFromGradeSummary(selectedSemester, forceRefresh) + .switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh) + .map { grades -> + grades.map { it.changeModifier(plusModifier, minusModifier) } + .groupBy { it.subject } + .mapValues { it.value.calcAverage() } + }) + } + + private fun getAverageFromGradeSummary(selectedSemester: Semester, forceRefresh: Boolean): Maybe> { + return gradeSummaryRepository.getGradesSummary(selectedSemester, forceRefresh) + .toMaybe() + .flatMap { + if (it.any { summary -> summary.average != .0 }) { + Maybe.just(it.map { summary -> summary.subject to summary.average }.toMap()) + } else Maybe.empty() + } + } +} 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 new file mode 100644 index 00000000..eb5e3669 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt @@ -0,0 +1,164 @@ +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.BaseFragment +import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter +import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment +import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment +import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment +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 : BaseFragment(), GradeView, MainView.MainChildView, MainView.TitledView { + + @Inject + lateinit var presenter: GradePresenter + + @Inject + lateinit var pagerAdapter: BaseFragmentPagerAdapter + + private var semesterSwitchMenu: MenuItem? = null + + companion object { + private const val SAVED_SEMESTER_KEY = "CURRENT_SEMESTER" + + fun newInstance() = GradeFragment() + } + + override val titleStringId: Int + get() = R.string.grade_title + + override val currentPageIndex: Int + get() = gradeViewPager.currentItem + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_grade, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SEMESTER_KEY)) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.action_menu_grade, menu) + semesterSwitchMenu = menu.findItem(R.id.gradeMenuSemester) + presenter.onCreateMenu() + } + + override fun initView() { + pagerAdapter.apply { + containerId = gradeViewPager.id + addFragmentsWithTitle(mapOf( + GradeDetailsFragment.newInstance() to getString(R.string.all_details), + GradeSummaryFragment.newInstance() to getString(R.string.grade_menu_summary), + GradeStatisticsFragment.newInstance() to getString(R.string.grade_menu_statistics) + )) + } + + gradeViewPager.run { + adapter = pagerAdapter + offscreenPageLimit = 3 + setOnSelectPageListener { presenter.onPageSelected(it) } + } + gradeTabLayout.setupWithViewPager(gradeViewPager) + gradeSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.gradeMenuSemester) presenter.onSemesterSwitch() + else false + } + + override fun onFragmentReselected() { + if (::presenter.isInitialized) presenter.onViewReselected() + } + + override fun showContent(show: Boolean) { + gradeViewPager.visibility = if (show) VISIBLE else INVISIBLE + gradeTabLayout.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showProgress(show: Boolean) { + gradeProgress.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showEmpty(show: Boolean) { + gradeEmpty.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showRefresh(show: Boolean) { + gradeSwipe.isRefreshing = show + } + + override fun showSemesterSwitch(show: Boolean) { + semesterSwitchMenu?.isVisible = show + } + + override fun enableSwipe(enable: Boolean) { + gradeSwipe.isEnabled = enable + } + + override fun showSemesterDialog(selectedIndex: Int) { + arrayOf(getString(R.string.grade_semester, 1), + getString(R.string.grade_semester, 2)).also { array -> + context?.let { + AlertDialog.Builder(it) + .setSingleChoiceItems(array, selectedIndex) { dialog, which -> + presenter.onSemesterSelected(which) + dialog.dismiss() + } + .setTitle(R.string.grade_switch_semester) + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .show() + } + } + } + + fun onChildRefresh() { + presenter.onChildViewRefresh() + } + + fun onChildFragmentLoaded(semesterId: Int) { + presenter.onChildViewLoaded(semesterId) + } + + override fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) { + (pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentLoadData(semesterId, forceRefresh) + } + + override fun notifyChildParentReselected(index: Int) { + (pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentReselected() + } + + override fun notifyChildSemesterChange(index: Int) { + (pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putInt(SAVED_SEMESTER_KEY, presenter.selectedIndex) + } + + override fun onDestroyView() { + super.onDestroyView() + presenter.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeModule.kt new file mode 100644 index 00000000..46a923d9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeModule.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.ui.modules.grade + +import dagger.Module +import dagger.Provides +import dagger.android.ContributesAndroidInjector +import io.github.wulkanowy.di.scopes.PerChildFragment +import io.github.wulkanowy.di.scopes.PerFragment +import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter +import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment +import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment +import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment + +@Module +abstract class GradeModule { + + @Module + companion object { + + @JvmStatic + @PerFragment + @Provides + fun provideGradeAdapter(fragment: GradeFragment) = BaseFragmentPagerAdapter(fragment.childFragmentManager) + } + + @PerChildFragment + @ContributesAndroidInjector + abstract fun bindGradeDetailsFragment(): GradeDetailsFragment + + @PerChildFragment + @ContributesAndroidInjector + abstract fun binGradeSummaryFragment(): GradeSummaryFragment + + @PerChildFragment + @ContributesAndroidInjector + abstract fun binGradeStatisticsFragment(): GradeStatisticsFragment +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt new file mode 100644 index 00000000..2ee69480 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt @@ -0,0 +1,130 @@ +package io.github.wulkanowy.ui.modules.grade + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject + +class GradePresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + var selectedIndex = 0 + private set + + private var semesters = emptyList() + + private val loadedSemesterId = mutableMapOf() + + fun onAttachView(view: GradeView, savedIndex: Int?) { + super.onAttachView(view) + selectedIndex = savedIndex ?: 0 + view.run { + initView() + enableSwipe(false) + } + Timber.i("Grade view was initialized with $selectedIndex index") + loadData() + } + + fun onCreateMenu() { + if (semesters.isEmpty()) view?.showSemesterSwitch(false) + } + + fun onViewReselected() { + Timber.i("Grade view is reselected") + view?.run { notifyChildParentReselected(currentPageIndex) } + } + + fun onSemesterSwitch(): Boolean { + if (semesters.isNotEmpty()) view?.showSemesterDialog(selectedIndex - 1) + return true + } + + fun onSemesterSelected(index: Int) { + if (selectedIndex != index - 1) { + Timber.i("Change semester in grade view to ${index + 1}") + selectedIndex = index + 1 + loadedSemesterId.clear() + view?.let { + notifyChildrenSemesterChange() + loadChild(it.currentPageIndex) + } + analytics.logEvent("changed_semester", "number" to index + 1) + } + } + + fun onChildViewRefresh() { + view?.let { loadChild(it.currentPageIndex, true) } + } + + fun onChildViewLoaded(semesterId: Int) { + view?.apply { + showContent(true) + showProgress(false) + showEmpty(false) + loadedSemesterId[currentPageIndex] = semesterId + } + } + + fun onPageSelected(index: Int) { + if (semesters.isNotEmpty()) loadChild(index) + } + + fun onSwipeRefresh() { + loadData() + } + + private fun loadData() { + Timber.i("Loading grade data started") + disposable.add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getSemesters(it) } + .doOnSuccess { + it.first { item -> item.isCurrent }.also { current -> + selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex + semesters = it.filter { semester -> semester.diaryId == current.diaryId } + } + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { view?.showRefresh(false) } + .subscribe({ + view?.run { + Timber.i("Loading grade result: Attempt load index $currentPageIndex") + loadChild(currentPageIndex) + enableSwipe(false) + showSemesterSwitch(true) + } + }) { + Timber.i("Loading grade result: An exception occurred") + errorHandler.dispatch(it) + view?.run { + showProgress(false) + showEmpty(true) + enableSwipe(true) + } + }) + } + + private fun loadChild(index: Int, forceRefresh: Boolean = false) { + semesters.first { it.semesterName == selectedIndex }.semesterId.also { + if (forceRefresh || loadedSemesterId[index] != it) { + Timber.i("Load grade child view index: $index") + view?.notifyChildLoadData(index, it, forceRefresh) + } + } + } + + private fun notifyChildrenSemesterChange() { + for (i in 0..2) view?.notifyChildSemesterChange(i) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt new file mode 100644 index 00000000..a37e6d67 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.ui.modules.grade + +import io.github.wulkanowy.ui.base.BaseView + +interface GradeView : BaseView { + + val currentPageIndex: Int + + fun initView() + + fun showContent(show: Boolean) + + fun showProgress(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showRefresh(show: Boolean) + + fun showSemesterSwitch(show: Boolean) + + fun showSemesterDialog(selectedIndex: Int) + + fun enableSwipe(enable: Boolean) + + fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) + + fun notifyChildParentReselected(index: Int) + + fun notifyChildSemesterChange(index: Int) + + interface GradeChildView { + + fun onParentChangeSemester() + + fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) + + fun onParentReselected() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt new file mode 100644 index 00000000..0abeaeea --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt @@ -0,0 +1,84 @@ +package io.github.wulkanowy.ui.modules.grade.details + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.utils.colorStringId +import io.github.wulkanowy.utils.getBackgroundColor +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.android.synthetic.main.dialog_grade.* + +class GradeDetailsDialog : DialogFragment() { + + private lateinit var grade: Grade + + private lateinit var colorScheme: String + + companion object { + private const val ARGUMENT_KEY = "Item" + private const val COLOR_SCHEME_KEY = "Scheme" + + fun newInstance(grade: Grade, colorScheme: String): GradeDetailsDialog { + return GradeDetailsDialog().apply { + arguments = Bundle().apply { + putSerializable(ARGUMENT_KEY, grade) + putString(COLOR_SCHEME_KEY, colorScheme) + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + grade = getSerializable(ARGUMENT_KEY) as Grade + colorScheme = getString(COLOR_SCHEME_KEY) ?: "default" + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.dialog_grade, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + + gradeDialogSubject.text = grade.subject + gradeDialogWeightValue.text = grade.weight + gradeDialogDateValue.text = grade.date.toFormattedString() + gradeDialogColorValue.text = getString(grade.colorStringId) + + gradeDialogCommentValue.apply { + if (grade.comment.isBlank()) { + visibility = GONE + gradeDialogComment.visibility = GONE + } else text = grade.comment + } + + gradeDialogValue.run { + text = grade.entry + setBackgroundResource(grade.getBackgroundColor(colorScheme)) + } + + gradeDialogTeacherValue.text = if (grade.teacher.isBlank()) { + getString(R.string.all_no_data) + } else grade.teacher + + gradeDialogDescriptionValue.text = grade.run { + when { + description.isBlank() && gradeSymbol.isNotBlank() -> gradeSymbol + description.isBlank() && gradeSymbol.isBlank() -> getString(R.string.all_no_description) + gradeSymbol.isNotBlank() && description.isNotBlank() -> "$gradeSymbol - $description" + else -> description + } + } + + gradeDialogClose.setOnClickListener { dismiss() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt new file mode 100644 index 00000000..d50fd057 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt @@ -0,0 +1,172 @@ +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.BaseFragment +import io.github.wulkanowy.ui.modules.grade.GradeFragment +import io.github.wulkanowy.ui.modules.grade.GradeView +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.fragment_grade_details.* +import javax.inject.Inject + +class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeChildView { + + @Inject + lateinit var presenter: GradeDetailsPresenter + + @Inject + lateinit var gradeDetailsAdapter: FlexibleAdapter> + + companion object { + fun newInstance() = GradeDetailsFragment() + } + + override val emptyAverageString: String + get() = getString(R.string.grade_no_average) + + override val averageString: String + get() = getString(R.string.grade_average) + + override val weightString: String + get() = getString(R.string.grade_weight) + + override val noDescriptionString: String + get() = getString(R.string.all_no_description) + + override val isViewEmpty + get() = gradeDetailsAdapter.isEmpty + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_grade_details, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + messageContainer = gradeDetailsRecycler + presenter.onAttachView(this) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.action_menu_grade_details, menu) + } + + override fun initView() { + gradeDetailsAdapter.run { + isAutoCollapseOnExpand = true + isAutoScrollOnExpand = true + setOnItemClickListener { presenter.onGradeItemSelected(it) } + } + + gradeDetailsRecycler.run { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = gradeDetailsAdapter + addItemDecoration(GradeDetailsHeaderItemDecoration(context) + .withDefaultDivider(R.layout.header_grade_details) + ) + } + gradeDetailsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.gradeDetailsMenuRead) presenter.onMarkAsReadSelected() + else false + } + + override fun updateData(data: List) { + gradeDetailsAdapter.updateDataSet(data, true) + } + + override fun updateItem(item: AbstractFlexibleItem<*>) { + gradeDetailsAdapter.updateItem(item) + } + + override fun clearView() { + gradeDetailsAdapter.clear() + } + + override fun collapseAllItems() { + gradeDetailsAdapter.collapseAll() + } + + override fun scrollToStart() { + gradeDetailsRecycler.scrollToPosition(0) + } + + override fun getHeaderOfItem(item: AbstractFlexibleItem<*>): IExpandable<*, out IFlexible<*>>? { + return gradeDetailsAdapter.getExpandableOf(item) + } + + override fun getGradeNumberString(number: Int): String { + return resources.getQuantityString(R.plurals.grade_number_item, number, number) + } + + override fun showProgress(show: Boolean) { + gradeDetailsProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + gradeDetailsSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + gradeDetailsRecycler.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showEmpty(show: Boolean) { + gradeDetailsEmpty.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showRefresh(show: Boolean) { + gradeDetailsSwipe.isRefreshing = show + } + + override fun showGradeDialog(grade: Grade, colorScheme: String) { + (activity as? MainActivity)?.showDialogFragment(GradeDetailsDialog.newInstance(grade, colorScheme)) + } + + override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { + presenter.onParentViewLoadData(semesterId, forceRefresh) + } + + override fun onParentReselected() { + presenter.onParentViewReselected() + } + + override fun onParentChangeSemester() { + presenter.onParentViewChangeSemester() + } + + override fun notifyParentDataLoaded(semesterId: Int) { + (parentFragment as? GradeFragment)?.onChildFragmentLoaded(semesterId) + } + + override fun notifyParentRefresh() { + (parentFragment as? GradeFragment)?.onChildRefresh() + } + + override fun onDestroyView() { + super.onDestroyView() + presenter.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt new file mode 100644 index 00000000..e5f6e824 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt @@ -0,0 +1,90 @@ +package io.github.wulkanowy.ui.modules.grade.details + +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractExpandableItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.ExpandableViewHolder +import io.github.wulkanowy.R +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.header_grade_details.* + +class GradeDetailsHeader( + private val subject: String, + private val number: String, + private val average: String, + var newGrades: Int, + private val isExpandable: Boolean +) : AbstractExpandableItem() { + + init { + isExpanded = !isExpandable + } + + override fun getLayoutRes() = R.layout.header_grade_details + + override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { + return ViewHolder(view, adapter) + } + + override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder, position: Int, payloads: MutableList?) { + holder.run { + gradeHeaderSubject.apply { + text = subject + maxLines = if (isExpanded) 2 else 1 + } + gradeHeaderAverage.text = average + gradeHeaderNumber.text = number + gradeHeaderNote.visibility = if (newGrades > 0) VISIBLE else GONE + gradeHeaderContainer.isEnabled = isExpandable + + isViewExpandable = isExpandable + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as GradeDetailsHeader + + if (subject != other.subject) return false + if (number != other.number) return false + if (average != other.average) return false + if (isExpandable != other.isExpandable) return false + + return true + } + + override fun hashCode(): Int { + var result = subject.hashCode() + result = 31 * result + number.hashCode() + result = 31 * result + average.hashCode() + result = 31 * result + isExpandable.hashCode() + return result + } + + class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : + ExpandableViewHolder(view, adapter), LayoutContainer { + + var isViewExpandable = true + + init { + contentView.setOnClickListener(this) + } + + override val containerView: View + get() = contentView + + override fun isViewCollapsibleOnClick() = isViewExpandable + + override fun isViewExpandableOnClick() = isViewExpandable + + override fun onClick(view: View?) { + super.onClick(view) + mAdapter.getItem(adapterPosition)?.let { mAdapter.updateItem(it) } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeaderItemDecoration.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeaderItemDecoration.kt new file mode 100644 index 00000000..39a911e6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeaderItemDecoration.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.ui.modules.grade.details + +import android.content.Context +import android.graphics.Canvas +import androidx.recyclerview.widget.RecyclerView +import eu.davidea.flexibleadapter.common.FlexibleItemDecoration + +class GradeDetailsHeaderItemDecoration(context: Context) : FlexibleItemDecoration(context) { + + override fun drawVertical(canvas: Canvas, parent: RecyclerView) { + canvas.save() + val left: Int + val right: Int + if (parent.clipToPadding) { + left = parent.paddingLeft + right = parent.width - parent.paddingRight + canvas.clipRect(left, parent.paddingTop, right, + parent.height - parent.paddingBottom) + } else { + left = 0 + right = parent.width + } + + val itemCount = parent.childCount + for (i in 1 until itemCount) { + val child = parent.getChildAt(i) + val viewHolder = parent.getChildViewHolder(child) + if (shouldDrawDivider(viewHolder)) { + parent.getDecoratedBoundsWithMargins(child, mBounds) + val bottom = mBounds.top + Math.round(child.translationY) + val top = bottom - mDivider.intrinsicHeight + mDivider.setBounds(left, top, right, bottom) + mDivider.draw(canvas) + } + } + canvas.restore() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt new file mode 100644 index 00000000..1e47eca5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt @@ -0,0 +1,74 @@ +package io.github.wulkanowy.ui.modules.grade.details + +import android.annotation.SuppressLint +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_grade_details.* + +class GradeDetailsItem( + val grade: Grade, + private val valueBgColor: Int, + private val weightString: String, + private val noDescriptionString: String +) : AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_grade_details + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { + return ViewHolder(view, adapter) + } + + @SuppressLint("SetTextI18n") + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { + holder.run { + gradeItemValue.run { + text = grade.entry + setBackgroundResource(valueBgColor) + } + gradeItemDescription.text = when { + grade.description.isNotBlank() -> grade.description + grade.gradeSymbol.isNotBlank() -> grade.gradeSymbol + else -> noDescriptionString + } + gradeItemDate.text = grade.date.toFormattedString() + gradeItemWeight.text = "$weightString: ${grade.weight}" + gradeItemNote.visibility = if (!grade.isRead) VISIBLE else GONE + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as GradeDetailsItem + + if (grade != other.grade) return false + if (grade.id != other.grade.id) return false + if (weightString != other.weightString) return false + if (valueBgColor != other.valueBgColor) return false + + return true + } + + override fun hashCode(): Int { + var result = grade.hashCode() + result = 31 * result + grade.id.toInt() + result = 31 * result + weightString.hashCode() + result = 31 * result + valueBgColor + return result + } + + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt new file mode 100644 index 00000000..184527e2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -0,0 +1,192 @@ +package io.github.wulkanowy.ui.modules.grade.details + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.repositories.grade.GradeRepository +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.getBackgroundColor +import timber.log.Timber +import javax.inject.Inject + +class GradeDetailsPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val gradeRepository: GradeRepository, + private val semesterRepository: SemesterRepository, + private val preferencesRepository: PreferencesRepository, + private val averageProvider: GradeAverageProvider, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + private var currentSemesterId = 0 + + override fun onAttachView(view: GradeDetailsView) { + super.onAttachView(view) + view.initView() + } + + fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { + currentSemesterId = semesterId + loadData(semesterId, forceRefresh) + } + + fun onGradeItemSelected(item: AbstractFlexibleItem<*>?) { + if (item is GradeDetailsItem) { + Timber.i("Select grade item ${item.grade.id}") + view?.apply { + showGradeDialog(item.grade, preferencesRepository.gradeColorTheme) + if (!item.grade.isRead) { + item.grade.isRead = true + updateItem(item) + getHeaderOfItem(item)?.let { header -> + if (header is GradeDetailsHeader) { + header.newGrades-- + updateItem(header) + } + } + updateGrade(item.grade) + } + } + } + } + + fun onMarkAsReadSelected(): Boolean { + Timber.i("Select mark grades as read") + disposable.add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getSemesters(it) } + .flatMap { gradeRepository.getUnreadGrades(it.first { item -> item.semesterId == currentSemesterId }) } + .map { it.map { grade -> grade.apply { isRead = true } } } + .flatMapCompletable { + Timber.i("Mark as read ${it.size} grades") + gradeRepository.updateGrades(it) + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + Timber.i("Mark as read result: Success") + loadData(currentSemesterId, false) + }, { + Timber.i("Mark as read result: An exception occurred") + errorHandler.dispatch(it) + })) + return true + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the grade details") + view?.notifyParentRefresh() + } + + fun onParentViewReselected() { + view?.run { + if (!isViewEmpty) { + if (preferencesRepository.isGradeExpandable) collapseAllItems() + scrollToStart() + } + } + } + + fun onParentViewChangeSemester() { + view?.run { + showProgress(true) + enableSwipe(false) + showRefresh(false) + showContent(false) + showEmpty(false) + clearView() + } + disposable.clear() + } + + private fun loadData(semesterId: Int, forceRefresh: Boolean) { + Timber.i("Loading grade details data started") + disposable.add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getSemesters(it).map { semester -> it to semester } } + .flatMap { (student, semesters) -> + averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh) + .flatMap { averages -> + gradeRepository.getGrades(student, semesters.first { semester -> semester.semesterId == semesterId }) + .map { it.sortedByDescending { grade -> grade.date } } + .map { it.groupBy { grade -> grade.subject }.toSortedMap() } + .map { createGradeItems(it, averages) } + } + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded(semesterId) + } + } + .subscribe({ + Timber.i("Loading grade details result: Success") + view?.run { + showEmpty(it.isEmpty()) + showContent(it.isNotEmpty()) + updateData(it) + } + analytics.logEvent("load_grade_details", "items" to it.size, "force_refresh" to forceRefresh) + }) { + Timber.i("Loading grade details result: An exception occurred") + view?.run { showEmpty(isViewEmpty) } + errorHandler.dispatch(it) + }) + } + + private fun createGradeItems(items: Map>, averages: Map): List { + val isGradeExpandable = preferencesRepository.isGradeExpandable + val gradeColorTheme = preferencesRepository.gradeColorTheme + + val noDescriptionString = view?.noDescriptionString.orEmpty() + val weightString = view?.weightString.orEmpty() + + return items.map { + GradeDetailsHeader( + subject = it.key, + average = formatAverage(averages[it.key]), + number = view?.getGradeNumberString(it.value.size).orEmpty(), + newGrades = it.value.filter { grade -> !grade.isRead }.size, + isExpandable = isGradeExpandable + ).apply { + subItems = it.value.map { item -> + GradeDetailsItem( + grade = item, + valueBgColor = item.getBackgroundColor(gradeColorTheme), + weightString = weightString, + noDescriptionString = noDescriptionString + ) + } + } + } + } + + private fun formatAverage(average: Double?): String { + return view?.run { + if (average == null || average == .0) emptyAverageString + else averageString.format(average) + }.orEmpty() + } + + private fun updateGrade(grade: Grade) { + Timber.i("Attempt to update grade ${grade.id}") + disposable.add(gradeRepository.updateGrade(grade) + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ Timber.i("Update grade result: Success") }) + { error -> + Timber.i("Update grade result: An exception occurred") + errorHandler.dispatch(error) + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt new file mode 100644 index 00000000..88b83bda --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt @@ -0,0 +1,52 @@ +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.BaseView + +interface GradeDetailsView : BaseView { + + val isViewEmpty: Boolean + + val emptyAverageString: String + + val averageString: String + + val weightString: String + + val noDescriptionString: String + + fun initView() + + fun updateData(data: List) + + fun updateItem(item: AbstractFlexibleItem<*>) + + fun clearView() + + fun scrollToStart() + + fun collapseAllItems() + + fun showGradeDialog(grade: Grade, colorScheme: String) + + fun showContent(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showRefresh(show: Boolean) + + fun notifyParentDataLoaded(semesterId: Int) + + fun notifyParentRefresh() + + fun getGradeNumberString(number: Int): String + + fun getHeaderOfItem(item: AbstractFlexibleItem<*>): IExpandable<*, out IFlexible<*>>? +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt new file mode 100644 index 00000000..a1cea6df --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt @@ -0,0 +1,214 @@ +package io.github.wulkanowy.ui.modules.grade.statistics + +import android.graphics.Color.WHITE +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.TextView +import androidx.core.content.ContextCompat +import com.github.mikephil.charting.components.Legend +import com.github.mikephil.charting.components.LegendEntry +import com.github.mikephil.charting.data.PieData +import com.github.mikephil.charting.data.PieDataSet +import com.github.mikephil.charting.data.PieEntry +import com.github.mikephil.charting.formatter.ValueFormatter +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.GradeStatistics +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.grade.GradeFragment +import io.github.wulkanowy.ui.modules.grade.GradeView +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.setOnItemSelectedListener +import kotlinx.android.synthetic.main.fragment_grade_statistics.* +import javax.inject.Inject + +class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.GradeChildView { + + @Inject + lateinit var presenter: GradeStatisticsPresenter + + private lateinit var subjectsAdapter: ArrayAdapter + + companion object { + private const val SAVED_CHART_TYPE = "CURRENT_TYPE" + + fun newInstance() = GradeStatisticsFragment() + } + + override val isViewEmpty + get() = gradeStatisticsChart.isEmpty + + private lateinit var gradeColors: List> + + private val vulcanGradeColors = listOf( + 6 to R.color.grade_vulcan_six, + 5 to R.color.grade_vulcan_five, + 4 to R.color.grade_vulcan_four, + 3 to R.color.grade_vulcan_three, + 2 to R.color.grade_vulcan_two, + 1 to R.color.grade_vulcan_one + ) + + private val materialGradeColors = listOf( + 6 to R.color.grade_material_six, + 5 to R.color.grade_material_five, + 4 to R.color.grade_material_four, + 3 to R.color.grade_material_three, + 2 to R.color.grade_material_two, + 1 to R.color.grade_material_one + ) + + private val gradeLabels = listOf( + "6, 6-", "5, 5-, 5+", "4, 4-, 4+", "3, 3-, 3+", "2, 2-, 2+", "1, 1+" + ) + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_grade_statistics, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + messageContainer = gradeStatisticsChart + presenter.onAttachView(this, savedInstanceState?.getBoolean(SAVED_CHART_TYPE)) + } + + override fun initView() { + gradeStatisticsChart.run { + description.isEnabled = false + setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground)) + setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary)) + animateXY(1000, 1000) + minAngleForSlices = 25f + legend.apply { + textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary) + } + } + + context?.let { + subjectsAdapter = ArrayAdapter(it, android.R.layout.simple_spinner_item, ArrayList()) + subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject) + } + + gradeStatisticsSubjects.run { + adapter = subjectsAdapter + setOnItemSelectedListener { presenter.onSubjectSelected((it as TextView).text.toString()) } + } + + gradeStatisticsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + } + + override fun updateSubjects(data: ArrayList) { + subjectsAdapter.run { + clear() + addAll(data) + notifyDataSetChanged() + } + } + + override fun updateData(items: List, theme: String) { + gradeColors = when (theme) { + "vulcan" -> vulcanGradeColors + else -> materialGradeColors + } + + gradeStatisticsChart.run { + data = PieData(PieDataSet(items.map { + PieEntry(it.amount.toFloat(), it.grade.toString()) + }, "Legenda").apply { + valueTextSize = 12f + sliceSpace = 1f + valueTextColor = WHITE + setColors(items.map { + gradeColors.single { color -> color.first == it.grade }.second + }.toIntArray(), context) + }).apply { + setTouchEnabled(false) + setValueFormatter(object : ValueFormatter() { + override fun getPieLabel(value: Float, pieEntry: PieEntry): String { + return resources.getQuantityString(R.plurals.grade_number_item, value.toInt(), value.toInt()) + } + }) + centerText = items.fold(0) { acc, it -> acc + it.amount } + .let { resources.getQuantityString(R.plurals.grade_number_item, it, it) } + } + legend.apply { + setCustom(gradeLabels.mapIndexed { i, it -> + LegendEntry().apply { + label = it + formColor = ContextCompat.getColor(context, gradeColors[i].second) + form = Legend.LegendForm.SQUARE + } + }) + } + invalidate() + } + } + + override fun showSubjects(show: Boolean) { + gradeStatisticsSubjectsContainer.visibility = if (show) View.VISIBLE else View.INVISIBLE + gradeStatisticsTypeSwitch.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun clearView() { + gradeStatisticsChart.clear() + } + + override fun showContent(show: Boolean) { + gradeStatisticsChart.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showEmpty(show: Boolean) { + gradeStatisticsEmpty.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showProgress(show: Boolean) { + gradeStatisticsProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun enableSwipe(enable: Boolean) { + gradeStatisticsSwipe.isEnabled = enable + } + + override fun showRefresh(show: Boolean) { + gradeStatisticsSwipe.isRefreshing = show + } + + override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { + presenter.onParentViewLoadData(semesterId, forceRefresh) + } + + override fun onParentReselected() { + // + } + + override fun onParentChangeSemester() { + presenter.onParentViewChangeSemester() + } + + override fun notifyParentDataLoaded(semesterId: Int) { + (parentFragment as? GradeFragment)?.onChildFragmentLoaded(semesterId) + } + + override fun notifyParentRefresh() { + (parentFragment as? GradeFragment)?.onChildRefresh() + } + + override fun onResume() { + super.onResume() + gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId -> + presenter.onTypeChange(checkedId == R.id.gradeStatisticsTypeSemester) + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean(GradeStatisticsFragment.SAVED_CHART_TYPE, presenter.currentIsSemester) + } + + override fun onDestroyView() { + super.onDestroyView() + presenter.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt new file mode 100644 index 00000000..55ea611f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt @@ -0,0 +1,147 @@ +package io.github.wulkanowy.ui.modules.grade.statistics + +import io.github.wulkanowy.data.db.entities.Subject +import io.github.wulkanowy.data.repositories.gradestatistics.GradeStatisticsRepository +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.subject.SubjectRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject + +class GradeStatisticsPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val gradeStatisticsRepository: GradeStatisticsRepository, + private val subjectRepository: SubjectRepository, + private val semesterRepository: SemesterRepository, + private val preferencesRepository: PreferencesRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + private var subjects = emptyList() + + private var currentSemesterId = 0 + + private var currentSubjectName: String = "Wszystkie" + + var currentIsSemester = false + private set + + fun onAttachView(view: GradeStatisticsView, isSemester: Boolean?) { + super.onAttachView(view) + currentIsSemester = isSemester ?: false + view.initView() + } + + fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { + currentSemesterId = semesterId + loadSubjects() + loadData(semesterId, currentSubjectName, currentIsSemester, forceRefresh) + } + + fun onParentViewChangeSemester() { + view?.run { + showProgress(true) + enableSwipe(false) + showRefresh(false) + showContent(false) + showEmpty(false) + clearView() + } + disposable.clear() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the grade stats") + view?.notifyParentRefresh() + } + + fun onSubjectSelected(name: String) { + Timber.i("Select grade stats subject $name") + view?.run { + showContent(false) + showProgress(true) + enableSwipe(false) + showEmpty(false) + clearView() + } + (subjects.singleOrNull { it.name == name }?.name).let { + if (it != currentSubjectName) loadData(currentSemesterId, name, currentIsSemester) + } + } + + fun onTypeChange(isSemester: Boolean) { + Timber.i("Select grade stats semester: $isSemester") + disposable.clear() + view?.run { + showContent(false) + showProgress(true) + enableSwipe(false) + showEmpty(false) + clearView() + } + loadData(currentSemesterId, currentSubjectName, isSemester) + } + + private fun loadSubjects() { + Timber.i("Loading grade stats subjects started") + disposable.add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { subjectRepository.getSubjects(it) } + .doOnSuccess { subjects = it } + .map { ArrayList(it.map { subject -> subject.name }) } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + Timber.i("Loading grade stats subjects result: Success") + view?.run { + updateSubjects(it) + showSubjects(true) + } + }, { + Timber.e("Loading grade stats subjects result: An exception occurred") + errorHandler.dispatch(it) + }) + ) + } + + private fun loadData(semesterId: Int, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false) { + Timber.i("Loading grade stats data started") + currentSubjectName = subjectName + currentIsSemester = isSemester + disposable.add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getSemesters(it) } + .flatMap { gradeStatisticsRepository.getGradesStatistics(it.first { item -> item.semesterId == semesterId }, subjectName, isSemester, forceRefresh) } + .map { list -> list.sortedByDescending { it.grade } } + .map { list -> list.filter { it.amount != 0 } } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded(semesterId) + } + } + .subscribe({ + Timber.i("Loading grade stats result: Success") + view?.run { + showEmpty(it.isEmpty()) + showContent(it.isNotEmpty()) + updateData(it, preferencesRepository.gradeColorTheme) + } + analytics.logEvent("load_grade_statistics", "items" to it.size, "force_refresh" to forceRefresh) + }) { + Timber.e("Loading grade stats result: An exception occurred") + view?.run { showEmpty(isViewEmpty) } + errorHandler.dispatch(it) + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt new file mode 100644 index 00000000..edb2552e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.ui.modules.grade.statistics + +import io.github.wulkanowy.data.db.entities.GradeStatistics +import io.github.wulkanowy.ui.base.BaseView + +interface GradeStatisticsView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateSubjects(data: ArrayList) + + fun updateData(items: List, theme: String) + + fun showSubjects(show: Boolean) + + fun notifyParentDataLoaded(semesterId: Int) + + fun notifyParentRefresh() + + fun clearView() + + fun showContent(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showRefresh(show: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt new file mode 100644 index 00000000..7699a641 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt @@ -0,0 +1,121 @@ +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.BaseFragment +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 : BaseFragment(), GradeSummaryView, GradeView.GradeChildView { + + @Inject + lateinit var presenter: GradeSummaryPresenter + + @Inject + lateinit var gradeSummaryAdapter: FlexibleAdapter> + + companion object { + fun newInstance() = GradeSummaryFragment() + } + + override val isViewEmpty + get() = gradeSummaryAdapter.isEmpty + + override val predictedString + get() = getString(R.string.grade_summary_predicted_grade) + + override val finalString + get() = getString(R.string.grade_summary_final_grade) + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_grade_summary, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + messageContainer = gradeSummaryRecycler + presenter.onAttachView(this) + } + + override fun initView() { + gradeSummaryAdapter.setDisplayHeadersAtStartUp(true) + + gradeSummaryRecycler.run { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = gradeSummaryAdapter + } + gradeSummarySwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + } + + override fun updateData(data: List, header: GradeSummaryScrollableHeader) { + gradeSummaryAdapter.apply { + updateDataSet(data, true) + removeAllScrollableHeaders() + addScrollableHeader(header) + } + } + + override fun clearView() { + gradeSummaryAdapter.clear() + } + + override fun resetView() { + gradeSummaryRecycler.scrollToPosition(0) + } + + override fun showContent(show: Boolean) { + gradeSummaryRecycler.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showEmpty(show: Boolean) { + gradeSummaryEmpty.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showProgress(show: Boolean) { + gradeSummaryProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + gradeSummarySwipe.isEnabled = enable + } + + override fun showRefresh(show: Boolean) { + gradeSummarySwipe.isRefreshing = show + } + + override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { + presenter.onParentViewLoadData(semesterId, forceRefresh) + } + + override fun onParentReselected() { + presenter.onParentViewReselected() + } + + override fun onParentChangeSemester() { + presenter.onParentViewChangeSemester() + } + + override fun notifyParentDataLoaded(semesterId: Int) { + (parentFragment as? GradeFragment)?.onChildFragmentLoaded(semesterId) + } + + override fun notifyParentRefresh() { + (parentFragment as? GradeFragment)?.onChildRefresh() + } + + override fun onDestroyView() { + super.onDestroyView() + presenter.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt new file mode 100644 index 00000000..95c32d17 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt @@ -0,0 +1,64 @@ +package io.github.wulkanowy.ui.modules.grade.summary + +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.GradeSummary +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_grade_summary.* + +class GradeSummaryItem( + val summary: GradeSummary, + private val average: String +) : AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_grade_summary + + 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 { + gradeSummaryItemTitle.text = summary.subject + gradeSummaryItemPoints.text = summary.pointsSum + gradeSummaryItemAverage.text = average + gradeSummaryItemPredicted.text = "${summary.predictedGrade} ${summary.proposedPoints}".trim() + gradeSummaryItemFinal.text = "${summary.finalGrade} ${summary.finalPoints}".trim() + + gradeSummaryItemPointsContainer.visibility = if (summary.pointsSum.isBlank()) GONE else VISIBLE + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as GradeSummaryItem + + if (average != other.average) return false + if (summary != other.summary) return false + if (summary.id != other.summary.id) return false + + return true + } + + override fun hashCode(): Int { + var result = summary.hashCode() + result = 31 * result + summary.id.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/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt new file mode 100644 index 00000000..3f8fd0ed --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt @@ -0,0 +1,120 @@ +package io.github.wulkanowy.ui.modules.grade.summary + +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.calcAverage +import timber.log.Timber +import java.lang.String.format +import java.util.Locale.FRANCE +import javax.inject.Inject + +class GradeSummaryPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val gradeSummaryRepository: GradeSummaryRepository, + private val semesterRepository: SemesterRepository, + private val averageProvider: GradeAverageProvider, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: GradeSummaryView) { + super.onAttachView(view) + view.initView() + } + + fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { + Timber.i("Loading grade summary data started") + disposable.add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getSemesters(it).map { semesters -> it to semesters } } + .flatMap { (student, semesters) -> + gradeSummaryRepository.getGradesSummary(semesters.first { it.semesterId == semesterId }, forceRefresh) + .map { it.sortedBy { subject -> subject.subject } } + .flatMap { gradesSummary -> + averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh) + .map { averages -> createGradeSummaryItemsAndHeader(gradesSummary, averages) } + } + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded(semesterId) + } + }.subscribe({ (gradeSummaryItems, gradeSummaryHeader) -> + Timber.i("Loading grade summary result: Success") + view?.run { + showEmpty(gradeSummaryItems.isEmpty()) + showContent(gradeSummaryItems.isNotEmpty()) + updateData(gradeSummaryItems, gradeSummaryHeader) + } + analytics.logEvent("load_grade_summary", "items" to gradeSummaryItems.size, "force_refresh" to forceRefresh) + }) { + Timber.i("Loading grade summary result: An exception occurred") + view?.run { showEmpty(isViewEmpty) } + errorHandler.dispatch(it) + }) + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the grade summary") + view?.notifyParentRefresh() + } + + fun onParentViewReselected() { + view?.run { + if (!isViewEmpty) resetView() + } + } + + fun onParentViewChangeSemester() { + view?.run { + showProgress(true) + enableSwipe(false) + showRefresh(false) + showContent(false) + showEmpty(false) + clearView() + } + disposable.clear() + } + + private fun createGradeSummaryItemsAndHeader(gradesSummary: List, averages: Map) + : Pair, GradeSummaryScrollableHeader> { + return averages.filterValues { value -> value != 0.0 } + .let { filteredAverages -> + gradesSummary.filter { !checkEmpty(it, filteredAverages) } + .map { + GradeSummaryItem( + summary = it, + average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, "") + ) + }.let { + it to GradeSummaryScrollableHeader( + formatAverage(gradesSummary.calcAverage()), + formatAverage(filteredAverages.values.average())) + } + } + } + + 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 new file mode 100644 index 00000000..f1c535c7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryScrollableHeader.kt @@ -0,0 +1,53 @@ +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 new file mode 100644 index 00000000..9e9c6e58 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.ui.modules.grade.summary + +import io.github.wulkanowy.ui.base.BaseView + +interface GradeSummaryView : BaseView { + + val isViewEmpty: Boolean + + val predictedString: String + + val finalString: String + + fun initView() + + fun updateData(data: List, header: GradeSummaryScrollableHeader) + + fun resetView() + + fun clearView() + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showRefresh(show: Boolean) + + fun showContent(show: Boolean) + + fun showEmpty(show: Boolean) + + fun notifyParentDataLoaded(semesterId: Int) + + fun notifyParentRefresh() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkDialog.kt new file mode 100644 index 00000000..42784188 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkDialog.kt @@ -0,0 +1,49 @@ +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 new file mode 100644 index 00000000..01fdc909 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt @@ -0,0 +1,120 @@ +package io.github.wulkanowy.ui.modules.homework + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.FlexibleItemDecoration +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.fragment_homework.* +import javax.inject.Inject + +class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView { + + @Inject + lateinit var presenter: HomeworkPresenter + + @Inject + lateinit var homeworkAdapter: FlexibleAdapter> + + companion object { + private const val SAVED_DATE_KEY = "CURRENT_DATE" + + fun newInstance() = HomeworkFragment() + } + + override val titleStringId: Int + get() = R.string.homework_title + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_homework, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + messageContainer = homeworkRecycler + presenter.onAttachView(this, savedInstanceState?.getLong(HomeworkFragment.SAVED_DATE_KEY)) + } + + override fun initView() { + homeworkAdapter.run { + setOnItemClickListener { presenter.onHomeworkItemSelected(it) } + } + + homeworkRecycler.run { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = homeworkAdapter + addItemDecoration(FlexibleItemDecoration(context) + .withDefaultDivider() + .withDrawDividerOnLastItem(false) + ) + } + homeworkSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + homeworkPreviousButton.setOnClickListener { presenter.onPreviousDay() } + homeworkNextButton.setOnClickListener { presenter.onNextDay() } + } + + override fun updateData(data: List) { + homeworkAdapter.updateDataSet(data, true) + } + + override fun clearData() { + homeworkAdapter.clear() + } + + override fun updateNavigationWeek(date: String) { + homeworkNavDate.text = date + } + + override fun isViewEmpty() = homeworkAdapter.isEmpty + + override fun hideRefresh() { + homeworkSwipe.isRefreshing = false + } + + override fun showEmpty(show: Boolean) { + homeworkEmpty.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showProgress(show: Boolean) { + homeworkProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun enableSwipe(enable: Boolean) { + homeworkSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + homeworkRecycler.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showPreButton(show: Boolean) { + homeworkPreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showNextButton(show: Boolean) { + homeworkNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showTimetableDialog(homework: Homework) { + (activity as? MainActivity)?.showDialogFragment(HomeworkDialog.newInstance(homework)) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(HomeworkFragment.SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkHeader.kt new file mode 100644 index 00000000..49023788 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkHeader.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.ui.modules.homework + +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractHeaderItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.ExpandableViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.utils.toFormattedString +import io.github.wulkanowy.utils.weekDayName +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.header_homework.* +import org.threeten.bp.LocalDate + +class HomeworkHeader(private val date: LocalDate) : AbstractHeaderItem() { + + override fun getLayoutRes() = R.layout.header_homework + + override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { + return ViewHolder(view, adapter) + } + + override fun bindViewHolder( + adapter: FlexibleAdapter>?, holder: HomeworkHeader.ViewHolder, + position: Int, payloads: MutableList? + ) { + holder.run { + homeworkHeaderDay.text = date.weekDayName.capitalize() + homeworkHeaderDate.text = date.toFormattedString() + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as HomeworkHeader + + if (date != other.date) return false + + return true + } + + override fun hashCode(): Int { + return date.hashCode() + } + + class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : ExpandableViewHolder(view, adapter), + LayoutContainer { + + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt new file mode 100644 index 00000000..2de9233f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.ui.modules.homework + +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractSectionableItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Homework +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_homework.* + +class HomeworkItem(header: HomeworkHeader, val homework: Homework) : + AbstractSectionableItem(header) { + + override fun getLayoutRes() = R.layout.item_homework + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { + return ViewHolder(view, adapter) + } + + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { + holder.apply { + homeworkItemSubject.text = homework.subject + homeworkItemTeacher.text = homework.teacher + homeworkItemContent.text = homework.content + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as HomeworkItem + + if (homework != other.homework) return false + return true + } + + override fun hashCode(): Int { + var result = homework.hashCode() + result = 31 * result + homework.id.toInt() + return result + } + + class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt new file mode 100644 index 00000000..682e87d4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt @@ -0,0 +1,122 @@ +package io.github.wulkanowy.ui.modules.homework + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.repositories.homework.HomeworkRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.friday +import io.github.wulkanowy.utils.isHolidays +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import io.github.wulkanowy.utils.toFormattedString +import org.threeten.bp.LocalDate +import timber.log.Timber +import java.util.concurrent.TimeUnit +import javax.inject.Inject + +class HomeworkPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val homeworkRepository: HomeworkRepository, + private val semesterRepository: SemesterRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + lateinit var currentDate: LocalDate + private set + + fun onAttachView(view: HomeworkView, date: Long?) { + super.onAttachView(view) + view.initView() + Timber.i("Homework view was initialized") + loadData(LocalDate.ofEpochDay(date ?: LocalDate.now().nextOrSameSchoolDay.toEpochDay())) + reloadView() + } + + fun onPreviousDay() { + loadData(currentDate.minusDays(7)) + reloadView() + } + + fun onNextDay() { + loadData(currentDate.plusDays(7)) + reloadView() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the homework") + loadData(currentDate, true) + } + + fun onHomeworkItemSelected(item: AbstractFlexibleItem<*>?) { + if (item is HomeworkItem) { + Timber.i("Select homework item ${item.homework.id}") + view?.showTimetableDialog(item.homework) + } + } + + private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { + Timber.i("Loading homework data started") + currentDate = date + disposable.apply { + clear() + add(studentRepository.getCurrentStudent() + .delay(200, TimeUnit.MILLISECONDS) + .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { homeworkRepository.getHomework(it, currentDate, currentDate, forceRefresh) } + .map { it.groupBy { homework -> homework.date }.toSortedMap() } + .map { createHomeworkItem(it) } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + } + } + .subscribe({ + Timber.i("Loading homework result: Success") + view?.apply { + updateData(it) + showEmpty(it.isEmpty()) + showContent(it.isNotEmpty()) + } + analytics.logEvent("load_homework", "items" to it.size, "force_refresh" to forceRefresh) + }) { + Timber.i("Loading homework result: An exception occurred") + view?.run { showEmpty(isViewEmpty()) } + errorHandler.dispatch(it) + }) + } + } + + private fun createHomeworkItem(items: Map>): List { + return items.flatMap { + HomeworkHeader(it.key).let { header -> + it.value.reversed().map { item -> HomeworkItem(header, item) } + } + } + } + + private fun reloadView() { + Timber.i("Reload homework view with the date ${currentDate.toFormattedString()}") + view?.apply { + showProgress(true) + enableSwipe(false) + showContent(false) + showEmpty(false) + clearData() + showNextButton(!currentDate.plusDays(7).isHolidays) + showPreButton(!currentDate.minusDays(7).isHolidays) + updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " + + currentDate.friday.toFormattedString("dd.MM")) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt new file mode 100644 index 00000000..977a5b73 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.ui.modules.homework + +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.ui.base.BaseView + +interface HomeworkView : BaseView { + + fun initView() + + fun updateData(data: List) + + fun clearData() + + fun updateNavigationWeek(date: String) + + fun isViewEmpty(): Boolean + + fun hideRefresh() + + fun showEmpty(show: Boolean) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showTimetableDialog(homework: Homework) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt new file mode 100644 index 00000000..5e707b8a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt @@ -0,0 +1,84 @@ +package io.github.wulkanowy.ui.modules.login + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter +import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment +import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment +import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment +import io.github.wulkanowy.utils.setOnSelectPageListener +import kotlinx.android.synthetic.main.activity_login.* +import javax.inject.Inject + +class LoginActivity : BaseActivity(), LoginView { + + @Inject + override lateinit var presenter: LoginPresenter + + @Inject + lateinit var loginAdapter: BaseFragmentPagerAdapter + + companion object { + fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java) + } + + override val currentViewIndex: Int + get() = loginViewpager.currentItem + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_login) + messageContainer = loginContainer + + presenter.onAttachView(this) + } + + override fun initAdapter() { + loginAdapter.apply { + containerId = loginViewpager.id + addFragments(listOf( + LoginFormFragment.newInstance(), + LoginSymbolFragment.newInstance(), + LoginStudentSelectFragment.newInstance() + )) + } + + loginViewpager.run { + offscreenPageLimit = 2 + adapter = loginAdapter + setOnSelectPageListener { presenter.onViewSelected(it) } + } + } + + override fun switchView(index: Int) { + loginViewpager.setCurrentItem(index, false) + } + + override fun showActionBar(show: Boolean) { + supportActionBar?.apply { if (show) show() else hide() } + } + + override fun onBackPressed() { + presenter.onBackPressed { super.onBackPressed() } + } + + override fun notifyInitSymbolFragment(loginData: Triple) { + (loginAdapter.getFragmentInstance(1) as? LoginSymbolFragment)?.onParentInitSymbolFragment(loginData) + } + + override fun notifyInitStudentSelectFragment(students: List) { + (loginAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment)?.onParentInitStudentSelectFragment(students) + } + + fun onFormFragmentAccountLogged(students: List, loginData: Triple) { + presenter.onFormViewAccountLogged(students, loginData) + } + + fun onSymbolFragmentAccountLogged(students: List) { + presenter.onSymbolViewAccountLogged(students) + } +} 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 new file mode 100644 index 00000000..a70ff2d6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.ui.modules.login + +import android.content.res.Resources +import android.database.sqlite.SQLiteConstraintException +import com.readystatesoftware.chuck.api.ChuckCollector +import io.github.wulkanowy.R +import io.github.wulkanowy.api.login.BadCredentialsException +import io.github.wulkanowy.ui.base.ErrorHandler +import javax.inject.Inject + +class LoginErrorHandler @Inject constructor( + resources: Resources, + chuckCollector: ChuckCollector +) : ErrorHandler(resources, chuckCollector) { + + var onBadCredentials: () -> Unit = {} + + var onStudentDuplicate: (String) -> Unit = {} + + override fun proceed(error: Throwable) { + when (error) { + is BadCredentialsException -> onBadCredentials() + is SQLiteConstraintException -> onStudentDuplicate(resources.getString(R.string.login_duplicate_student)) + else -> super.proceed(error) + } + } + + override fun clear() { + super.clear() + onBadCredentials = {} + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt new file mode 100644 index 00000000..7c3e7bac --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.ui.modules.login + +import dagger.Module +import dagger.Provides +import dagger.android.ContributesAndroidInjector +import io.github.wulkanowy.di.scopes.PerActivity +import io.github.wulkanowy.di.scopes.PerFragment +import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter +import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment +import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment +import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment + +@Module +internal abstract class LoginModule { + + @Module + companion object { + + @JvmStatic + @PerActivity + @Provides + fun provideLoginAdapter(activity: LoginActivity) = BaseFragmentPagerAdapter(activity.supportFragmentManager) + } + + @PerFragment + @ContributesAndroidInjector + abstract fun bindLoginFormFragment(): LoginFormFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindLoginSymbolFragment(): LoginSymbolFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindLoginSelectStudentFragment(): LoginStudentSelectFragment +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt new file mode 100644 index 00000000..87827152 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt @@ -0,0 +1,66 @@ +package io.github.wulkanowy.ui.modules.login + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject + +class LoginPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: LoginView) { + super.onAttachView(view) + view.run { + initAdapter() + showActionBar(false) + } + Timber.i("Login view was initialized") + } + + fun onFormViewAccountLogged(students: List, loginData: Triple) { + view?.apply { + if (students.isEmpty()) { + Timber.i("Switch to symbol form") + notifyInitSymbolFragment(loginData) + switchView(1) + } else { + Timber.i("Switch to student select") + notifyInitStudentSelectFragment(students) + switchView(2) + } + } + } + + fun onSymbolViewAccountLogged(students: List) { + view?.apply { + Timber.i("Switch to student select") + notifyInitStudentSelectFragment(students) + switchView(2) + } + } + + fun onViewSelected(index: Int) { + view?.apply { + when (index) { + 0, 1 -> showActionBar(false) + 2 -> showActionBar(true) + } + } + } + + fun onBackPressed(default: () -> Unit) { + Timber.i("Back pressed in login view") + view?.apply { + when (currentViewIndex) { + 1, 2 -> switchView(0) + else -> default() + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt new file mode 100644 index 00000000..58d356bb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.ui.modules.login + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.ui.base.BaseView + +interface LoginView : BaseView { + + val currentViewIndex: Int + + fun initAdapter() + + fun switchView(index: Int) + + fun showActionBar(show: Boolean) + + fun notifyInitSymbolFragment(loginData: Triple) + + fun notifyInitStudentSelectFragment(students: List) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt new file mode 100644 index 00000000..b17a1205 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt @@ -0,0 +1,155 @@ +package io.github.wulkanowy.ui.modules.login.form + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import android.view.inputmethod.EditorInfo.IME_ACTION_DONE +import android.view.inputmethod.EditorInfo.IME_NULL +import android.widget.ArrayAdapter +import io.github.wulkanowy.BuildConfig.VERSION_NAME +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.openInternetBrowser +import io.github.wulkanowy.utils.setOnItemSelectedListener +import io.github.wulkanowy.utils.setOnTextChangedListener +import io.github.wulkanowy.utils.showSoftInput +import kotlinx.android.synthetic.main.fragment_login_form.* +import javax.inject.Inject + +class LoginFormFragment : BaseFragment(), LoginFormView { + + @Inject + lateinit var presenter: LoginFormPresenter + + companion object { + fun newInstance() = LoginFormFragment() + } + + override val formNameValue: String + get() = loginFormName.text.toString() + + override val formPassValue: String + get() = loginFormPass.text.toString() + + override val formHostValue: String? + get() = resources.getStringArray(R.array.endpoints_values)[loginFormHost.selectedItemPosition] + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_login_form, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this) + } + + override fun initView() { + loginFormName.setOnTextChangedListener { presenter.onNameTextChanged() } + loginFormPass.setOnTextChangedListener { presenter.onPassTextChanged() } + loginFormHost.setOnItemSelectedListener { presenter.onHostSelected() } + loginFormSignIn.setOnClickListener { presenter.onSignInClick() } + loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() } + + loginFormPass.setOnEditorActionListener { _, id, _ -> + if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false + } + + context?.let { + loginFormHost.adapter = ArrayAdapter.createFromResource(it, R.array.endpoints_keys, android.R.layout.simple_spinner_item) + .apply { setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) } + } + } + + override fun setDefaultCredentials(name: String, pass: String) { + loginFormName.setText(name) + loginFormPass.setText(pass) + } + + override fun setErrorNameRequired() { + loginFormNameLayout.run { + requestFocus() + error = getString(R.string.login_field_required) + } + } + + override fun setErrorPassRequired(focus: Boolean) { + loginFormPassLayout.run { + if (focus) requestFocus() + error = getString(R.string.login_field_required) + } + } + + override fun setErrorPassInvalid(focus: Boolean) { + loginFormPassLayout.run { + if (focus) requestFocus() + error = getString(R.string.login_invalid_password) + } + } + + override fun setErrorPassIncorrect() { + loginFormPassLayout.run { + requestFocus() + error = getString(R.string.login_incorrect_password) + } + } + + override fun clearNameError() { + loginFormNameLayout.error = null + } + + override fun clearPassError() { + loginFormPassLayout.error = null + } + + override fun showSoftKeyboard() { + activity?.showSoftInput() + } + + override fun hideSoftKeyboard() { + activity?.hideSoftInput() + } + + override fun showProgress(show: Boolean) { + loginFormProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showContent(show: Boolean) { + loginFormContainer.visibility = if (show) VISIBLE else GONE + } + + @SuppressLint("SetTextI18n") + override fun showVersion() { + loginFormVersion.apply { + visibility = VISIBLE + text = "${getString(R.string.app_name)} $VERSION_NAME" + } + } + + override fun showPrivacyPolicy() { + loginFormPrivacyLink.visibility = VISIBLE + } + + override fun notifyParentAccountLogged(students: List) { + (activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple( + loginFormName.text.toString(), + loginFormPass.text.toString(), + resources.getStringArray(R.array.endpoints_values)[loginFormHost.selectedItemPosition] + )) + } + + override fun openPrivacyPolicyPage() { + context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage) + } + + 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 new file mode 100644 index 00000000..0fbfb329 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt @@ -0,0 +1,108 @@ +package io.github.wulkanowy.ui.modules.login.form + +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.modules.login.LoginErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Named + +class LoginFormPresenter @Inject constructor( + schedulers: SchedulersProvider, + studentRepository: StudentRepository, + private val loginErrorHandler: LoginErrorHandler, + private val analytics: FirebaseAnalyticsHelper, + @param:Named("isDebug") private val isDebug: Boolean +) : BasePresenter(loginErrorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: LoginFormView) { + super.onAttachView(view) + view.run { + initView() + if (isDebug) showVersion() else showPrivacyPolicy() + + loginErrorHandler.onBadCredentials = { + setErrorPassIncorrect() + showSoftKeyboard() + Timber.i("Entered wrong username or password") + } + } + } + + fun onPrivacyLinkClick() { + view?.openPrivacyPolicyPage() + } + + fun onHostSelected() { + view?.apply { + clearPassError() + clearNameError() + if (formHostValue?.contains("fakelog") == true) setDefaultCredentials("jan@fakelog.cf", "jan123") + } + } + + fun onPassTextChanged() { + view?.clearPassError() + } + + fun onNameTextChanged() { + view?.clearNameError() + } + + fun onSignInClick() { + val email = view?.formNameValue.orEmpty() + val password = view?.formPassValue.orEmpty() + val endpoint = view?.formHostValue.orEmpty() + + if (!validateCredentials(email, password)) return + + disposable.add(studentRepository.getStudents(email, password, endpoint) + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doOnSubscribe { + view?.apply { + hideSoftKeyboard() + showProgress(true) + showContent(false) + } + Timber.i("Login started") + } + .doFinally { + view?.apply { + showProgress(false) + showContent(true) + } + } + .subscribe({ + Timber.i("Login result: Success") + analytics.logEvent("registration_form", "success" to true, "students" to it.size, "endpoint" to endpoint, "error" to "No error") + view?.notifyParentAccountLogged(it) + }, { + Timber.i("Login result: An exception occurred") + analytics.logEvent("registration_form", "success" to false, "students" to -1, "endpoint" to endpoint, "error" to it.localizedMessage.ifEmpty { "No message" }) + loginErrorHandler.dispatch(it) + })) + } + + private fun validateCredentials(login: String, password: String): Boolean { + var isCorrect = true + + if (login.isEmpty()) { + view?.setErrorNameRequired() + isCorrect = false + } + + if (password.isEmpty()) { + view?.setErrorPassRequired(focus = isCorrect) + isCorrect = false + } + + if (password.length < 6 && password.isNotEmpty()) { + view?.setErrorPassInvalid(focus = isCorrect) + isCorrect = false + } + return isCorrect + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt new file mode 100644 index 00000000..80a7b5e9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt @@ -0,0 +1,45 @@ +package io.github.wulkanowy.ui.modules.login.form + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.ui.base.BaseView + +interface LoginFormView : BaseView { + + fun initView() + + val formNameValue: String + + val formPassValue: String + + val formHostValue: String? + + fun setDefaultCredentials(name: String, pass: String) + + fun setErrorNameRequired() + + fun setErrorPassRequired(focus: Boolean) + + fun setErrorPassInvalid(focus: Boolean) + + fun setErrorPassIncorrect() + + fun clearNameError() + + fun clearPassError() + + fun showSoftKeyboard() + + fun hideSoftKeyboard() + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun showVersion() + + fun showPrivacyPolicy() + + fun notifyParentAccountLogged(students: List) + + fun openPrivacyPolicyPage() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt new file mode 100644 index 00000000..5c48cf2f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt @@ -0,0 +1,87 @@ +package io.github.wulkanowy.ui.modules.login.studentselect + +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.Student +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.fragment_login_student_select.* +import java.io.Serializable +import javax.inject.Inject + +class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView { + + @Inject + lateinit var presenter: LoginStudentSelectPresenter + + @Inject + lateinit var loginAdapter: FlexibleAdapter> + + companion object { + const val SAVED_STUDENTS = "STUDENTS" + + fun newInstance() = LoginStudentSelectFragment() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_login_student_select, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this, savedInstanceState?.getSerializable(SAVED_STUDENTS)) + } + + override fun initView() { + loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() } + loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } } + + loginStudentSelectRecycler.apply { + adapter = loginAdapter + layoutManager = SmoothScrollLinearLayoutManager(context) + } + } + + override fun updateData(data: List) { + loginAdapter.updateDataSet(data) + } + + override fun openMainView() { + activity?.let { startActivity(MainActivity.getStartIntent(context = it, clear = true)) } + } + + override fun showProgress(show: Boolean) { + loginStudentSelectProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showContent(show: Boolean) { + loginStudentSelectContent.visibility = if (show) VISIBLE else GONE + } + + override fun enableSignIn(enable: Boolean) { + loginStudentSelectSignIn.isEnabled = enable + } + + fun onParentInitStudentSelectFragment(students: List) { + presenter.onParentInitStudentSelectView(students) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putSerializable(SAVED_STUDENTS, presenter.students as Serializable) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt new file mode 100644 index 00000000..27723c53 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt @@ -0,0 +1,59 @@ +package io.github.wulkanowy.ui.modules.login.studentselect + +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.Student +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_login_student_select.* + +class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_login_student_select + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ItemViewHolder { + return ItemViewHolder(view, adapter) + } + + @SuppressLint("SetTextI18n") + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ItemViewHolder, position: Int, payloads: MutableList) { + holder.apply { + loginItemName.text = "${student.studentName} ${student.className}" + loginItemSchool.text = student.schoolName + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as LoginStudentSelectItem + + if (student != other.student) return false + + return true + } + + override fun hashCode(): Int { + return student.hashCode() + } + + class ItemViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + + override val containerView: View + get() = itemView + + init { + loginItemCheck.setOnClickListener { super.onClick(loginItemContainer) } + } + + override fun onClick(view: View?) { + super.onClick(view) + loginItemCheck.apply { isChecked = !isChecked } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt new file mode 100644 index 00000000..0636e163 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt @@ -0,0 +1,93 @@ +package io.github.wulkanowy.ui.modules.login.studentselect + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.modules.login.LoginErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import java.io.Serializable +import javax.inject.Inject + +class LoginStudentSelectPresenter @Inject constructor( + schedulers: SchedulersProvider, + studentRepository: StudentRepository, + private val loginErrorHandler: LoginErrorHandler, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(loginErrorHandler, studentRepository, schedulers) { + + var students = emptyList() + + private var selectedStudents = mutableListOf() + + fun onAttachView(view: LoginStudentSelectView, students: Serializable?) { + super.onAttachView(view) + view.run { + initView() + enableSignIn(false) + loginErrorHandler.onStudentDuplicate = { + showMessage(it) + Timber.i("The student already registered in the app was selected") + } + } + + if (students is List<*> && students.isNotEmpty()) { + loadData(students.filterIsInstance()) + } + } + + fun onSignIn() { + registerStudents(selectedStudents) + } + + fun onParentInitStudentSelectView(students: List) { + loadData(students) + if (students.size == 1) registerStudents(students) + } + + fun onItemSelected(item: AbstractFlexibleItem<*>?) { + if (item is LoginStudentSelectItem) { + selectedStudents.removeAll { it == item.student } + .let { if (!it) selectedStudents.add(item.student) } + + view?.enableSignIn(selectedStudents.isNotEmpty()) + } + } + + private fun loadData(students: List) { + this.students = students + view?.apply { + updateData(students.map { LoginStudentSelectItem(it) }) + } + } + + private fun registerStudents(students: List) { + disposable.add(studentRepository.saveStudents(students) + .map { students.first().apply { id = it.first() } } + .flatMapCompletable { studentRepository.switchStudent(it) } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doOnSubscribe { + view?.apply { + showProgress(true) + showContent(false) + } + Timber.i("Registration started") + } + .subscribe({ + students.forEach { analytics.logEvent("registration_student_select", "success" to true, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to "No error") } + Timber.i("Registration result: Success") + view?.openMainView() + }, { error -> + students.forEach { analytics.logEvent("registration_student_select", "success" to false, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to error.localizedMessage.ifEmpty { "No message" }) } + Timber.i("Registration result: An exception occurred ") + loginErrorHandler.dispatch(error) + view?.apply { + showProgress(false) + showContent(true) + } + })) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt new file mode 100644 index 00000000..3967313c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.ui.modules.login.studentselect + +import io.github.wulkanowy.ui.base.BaseView + +interface LoginStudentSelectView : BaseView { + + fun initView() + + fun updateData(data: List) + + fun openMainView() + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun enableSignIn(enable: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt new file mode 100644 index 00000000..87de0a81 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt @@ -0,0 +1,116 @@ +package io.github.wulkanowy.ui.modules.login.symbol + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import android.view.inputmethod.EditorInfo.IME_ACTION_DONE +import android.view.inputmethod.EditorInfo.IME_NULL +import android.widget.ArrayAdapter +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.setOnTextChangedListener +import io.github.wulkanowy.utils.showSoftInput +import kotlinx.android.synthetic.main.fragment_login_symbol.* +import javax.inject.Inject + +class LoginSymbolFragment : BaseFragment(), LoginSymbolView { + + @Inject + lateinit var presenter: LoginSymbolPresenter + + companion object { + private const val SAVED_LOGIN_DATA = "LOGIN_DATA" + + fun newInstance() = LoginSymbolFragment() + } + + override val symbolNameError: CharSequence? + get() = loginSymbolNameLayout.error + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_login_symbol, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this, savedInstanceState?.getSerializable(SAVED_LOGIN_DATA)) + } + + override fun initView() { + loginSymbolSignIn.setOnClickListener { presenter.attemptLogin(loginSymbolName.text.toString()) } + + loginSymbolName.setOnTextChangedListener { presenter.onSymbolTextChanged() } + + loginSymbolName.apply { + setOnEditorActionListener { _, id, _ -> + if (id == IME_ACTION_DONE || id == IME_NULL) loginSymbolSignIn.callOnClick() else false + } + setAdapter(ArrayAdapter(context, android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values))) + } + } + + fun onParentInitSymbolFragment(loginData: Triple) { + presenter.onParentInitSymbolView(loginData) + } + + override fun setErrorSymbolIncorrect() { + loginSymbolNameLayout.apply { + requestFocus() + error = getString(R.string.login_incorrect_symbol) + } + } + + override fun setErrorSymbolRequire() { + loginSymbolNameLayout.apply { + requestFocus() + error = getString(R.string.login_field_required) + } + } + + override fun clearSymbolError() { + loginSymbolNameLayout.error = null + } + + override fun clearAndFocusSymbol() { + loginSymbolNameLayout.apply { + editText?.text = null + requestFocus() + } + } + + override fun showSoftKeyboard() { + activity?.showSoftInput() + } + + override fun hideSoftKeyboard() { + activity?.hideSoftInput() + } + + override fun showProgress(show: Boolean) { + loginSymbolProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showContent(show: Boolean) { + loginSymbolContainer.visibility = if (show) VISIBLE else GONE + } + + override fun notifyParentAccountLogged(students: List) { + (activity as? LoginActivity)?.onSymbolFragmentAccountLogged(students) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putSerializable(SAVED_LOGIN_DATA, presenter.loginData) + } + + override fun onDestroyView() { + super.onDestroyView() + presenter.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt new file mode 100644 index 00000000..8776526e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt @@ -0,0 +1,85 @@ +package io.github.wulkanowy.ui.modules.login.symbol + +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.modules.login.LoginErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import io.reactivex.Single +import timber.log.Timber +import java.io.Serializable +import javax.inject.Inject + +class LoginSymbolPresenter @Inject constructor( + studentRepository: StudentRepository, + schedulers: SchedulersProvider, + private val loginErrorHandler: LoginErrorHandler, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(loginErrorHandler, studentRepository, schedulers) { + + var loginData: Triple? = null + + @Suppress("UNCHECKED_CAST") + fun onAttachView(view: LoginSymbolView, savedLoginData: Serializable?) { + super.onAttachView(view) + view.initView() + if (savedLoginData is Triple<*, *, *>) { + loginData = savedLoginData as Triple + } + } + + fun onSymbolTextChanged() { + view?.apply { if (symbolNameError != null) clearSymbolError() } + } + + fun attemptLogin(symbol: String) { + if (symbol.isBlank()) { + view?.setErrorSymbolRequire() + return + } + + disposable.add( + Single.fromCallable { if (loginData == null) throw IllegalArgumentException("Login data is null") else loginData } + .flatMap { studentRepository.getStudents(it.first, it.second, it.third, symbol) } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doOnSubscribe { + view?.apply { + hideSoftKeyboard() + showProgress(true) + showContent(false) + } + Timber.i("Login with symbol started") + } + .doFinally { + view?.apply { + showProgress(false) + showContent(true) + } + } + .subscribe({ + analytics.logEvent("registration_symbol", "success" to true, "students" to it.size, "endpoint" to loginData?.third, "symbol" to symbol, "error" to "No error") + view?.apply { + if (it.isEmpty()) { + Timber.i("Login with symbol result: Empty student list") + setErrorSymbolIncorrect() + } else { + Timber.i("Login with symbol result: Success") + notifyParentAccountLogged(it) + } + } + }, { + Timber.i("Login with symbol result: An exception occurred") + analytics.logEvent("registration_symbol", "success" to false, "students" to -1, "endpoint" to loginData?.third, "symbol" to symbol, "error" to it.localizedMessage.ifEmpty { "No message" }) + loginErrorHandler.dispatch(it) + })) + } + + fun onParentInitSymbolView(loginData: Triple) { + this.loginData = loginData + view?.apply { + clearAndFocusSymbol() + showSoftKeyboard() + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt new file mode 100644 index 00000000..2e5143ef --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.ui.modules.login.symbol + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.ui.base.BaseView + +interface LoginSymbolView : BaseView { + + val symbolNameError: CharSequence? + + fun initView() + + fun setErrorSymbolIncorrect() + + fun setErrorSymbolRequire() + + fun clearSymbolError() + + fun clearAndFocusSymbol() + + fun showSoftKeyboard() + + fun hideSoftKeyboard() + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun notifyParentAccountLogged(students: List) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt new file mode 100644 index 00000000..00204a87 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt @@ -0,0 +1,71 @@ +package io.github.wulkanowy.ui.modules.luckynumber + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import kotlinx.android.synthetic.main.fragment_lucky_number.* +import javax.inject.Inject + +class LuckyNumberFragment : BaseFragment(), LuckyNumberView, MainView.TitledView { + + @Inject + lateinit var presenter: LuckyNumberPresenter + + companion object { + fun newInstance() = LuckyNumberFragment() + } + + override val titleStringId: Int + get() = R.string.lucky_number_title + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_lucky_number, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this) + } + + override fun initView() { + luckyNumberSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + } + + override fun updateData(data: LuckyNumber) { + luckyNumberText.text = data.luckyNumber.toString() + } + + override fun hideRefresh() { + luckyNumberSwipe.isRefreshing = false + } + + override fun showEmpty(show: Boolean) { + luckyNumberEmpty.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showProgress(show: Boolean) { + luckyNumberProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun enableSwipe(enable: Boolean) { + luckyNumberSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + luckyNumberContent.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun isViewEmpty(): Boolean { + return luckyNumberText.text.isBlank() + } + + override fun onDestroyView() { + super.onDestroyView() + presenter.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt new file mode 100644 index 00000000..ee7260e6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt @@ -0,0 +1,76 @@ +package io.github.wulkanowy.ui.modules.luckynumber + +import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject + +class LuckyNumberPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val luckyNumberRepository: LuckyNumberRepository, + private val semesterRepository: SemesterRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: LuckyNumberView) { + super.onAttachView(view) + view.run { + initView() + showContent(false) + enableSwipe(false) + } + Timber.i("Lucky number view was initialized") + loadData() + } + + private fun loadData(forceRefresh: Boolean = false) { + Timber.i("Loading lucky number started") + disposable.apply { + clear() + add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMapMaybe { luckyNumberRepository.getLuckyNumber(it, forceRefresh) } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + } + } + .subscribe({ + Timber.i("Loading lucky number result: Success") + view?.apply { + updateData(it) + showContent(true) + showEmpty(false) + } + analytics.logEvent("load_lucky_number", "lucky_number" to it.luckyNumber, "force_refresh" to forceRefresh) + }, { + Timber.i("Loading lucky number result: An exception occurred") + view?.run { showEmpty(isViewEmpty()) } + errorHandler.dispatch(it) + }, { + Timber.i("Loading lucky number result: No lucky number found") + view?.run { + showContent(false) + showEmpty(true) + } + }) + ) + } + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the lucky number") + loadData(true) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt new file mode 100644 index 00000000..9ead2b1f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.ui.modules.luckynumber + +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.ui.base.BaseView + +interface LuckyNumberView : BaseView { + + fun initView() + + fun updateData(data: LuckyNumber) + + fun hideRefresh() + + fun showEmpty(show: Boolean) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun isViewEmpty(): Boolean +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt new file mode 100644 index 00000000..3dd0e5df --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt @@ -0,0 +1,73 @@ +package io.github.wulkanowy.ui.modules.luckynumberwidget + +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +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.BaseActivity +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.activity_widget_configure.* +import javax.inject.Inject + +class LuckyNumberWidgetConfigureActivity : BaseActivity(), + LuckyNumberWidgetConfigureView { + + @Inject + lateinit var configureAdapter: FlexibleAdapter> + + @Inject + override lateinit var presenter: LuckyNumberWidgetConfigurePresenter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setResult(RESULT_CANCELED) + setContentView(R.layout.activity_widget_configure) + + intent.extras.let { + presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID)) + } + } + + override fun initView() { + widgetConfigureRecycler.apply { + adapter = configureAdapter + layoutManager = SmoothScrollLinearLayoutManager(context) + } + configureAdapter.setOnItemClickListener { presenter.onItemSelect(it) } + } + + override fun updateData(data: List) { + configureAdapter.updateDataSet(data) + } + + override fun updateLuckyNumberWidget(widgetId: Int) { + sendBroadcast(Intent(this, LuckyNumberWidgetProvider::class.java) + .apply { + action = ACTION_APPWIDGET_UPDATE + putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId)) + }) + } + + override fun setSuccessResult(widgetId: Int) { + setResult(RESULT_OK, Intent().apply { putExtra(EXTRA_APPWIDGET_ID, widgetId) }) + } + + override fun showError(text: String, error: Throwable) { + Toast.makeText(this, text, Toast.LENGTH_LONG).show() + } + + override fun finishView() { + finish() + } + + override fun openLoginView() { + startActivity(LoginActivity.getStartIntent(this)) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureItem.kt new file mode 100644 index 00000000..bba0974b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureItem.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.ui.modules.luckynumberwidget + +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.Student +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureItem +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_account.* + +class LuckyNumberWidgetConfigureItem(var student: Student, val isCurrent: Boolean) : + AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_account + + 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 { + accountItemName.text = "${student.studentName} ${student.className}" + accountItemSchool.text = student.schoolName + accountItemImage.setBackgroundResource(if (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 TimetableWidgetConfigureItem + + if (student != other.student) return false + + return true + } + + override fun hashCode(): Int { + var result = student.hashCode() + result = 31 * result + student.id.toInt() + return result + } + + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt new file mode 100644 index 00000000..2ed0fa6f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt @@ -0,0 +1,62 @@ +package io.github.wulkanowy.ui.modules.luckynumberwidget + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.SharedPrefHelper +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getStudentWidgetKey +import io.github.wulkanowy.utils.SchedulersProvider +import javax.inject.Inject + +class LuckyNumberWidgetConfigurePresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val sharedPref: SharedPrefHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + private var appWidgetId: Int? = null + + fun onAttachView(view: LuckyNumberWidgetConfigureView, appWidgetId: Int?) { + super.onAttachView(view) + this.appWidgetId = appWidgetId + view.initView() + loadData() + } + + fun onItemSelect(item: AbstractFlexibleItem<*>) { + if (item is LuckyNumberWidgetConfigureItem) { + registerStudent(item.student) + } + } + + private fun loadData() { + disposable.add(studentRepository.getSavedStudents(false) + .map { it to appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } } + .map { (students, currentStudentId) -> + students.map { student -> LuckyNumberWidgetConfigureItem(student, student.id == currentStudentId) } + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + when { + it.isEmpty() -> view?.openLoginView() + it.size == 1 -> registerStudent(it.single().student) + else -> view?.updateData(it) + } + }, { errorHandler.dispatch(it) })) + } + + private fun registerStudent(student: Student) { + appWidgetId?.also { + sharedPref.putLong(getStudentWidgetKey(it), student.id) + view?.apply { + updateLuckyNumberWidget(it) + setSuccessResult(it) + } + } + view?.finishView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt new file mode 100644 index 00000000..49c3f1dc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.ui.modules.luckynumberwidget + +import io.github.wulkanowy.ui.base.BaseView +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureItem + +interface LuckyNumberWidgetConfigureView : BaseView { + + fun initView() + + fun updateData(data: List) + + fun updateLuckyNumberWidget(widgetId: Int) + + fun setSuccessResult(widgetId: Int) + + fun finishView() + + fun openLoginView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt new file mode 100644 index 00000000..66141c91 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt @@ -0,0 +1,183 @@ +package io.github.wulkanowy.ui.modules.luckynumberwidget + +import android.annotation.TargetApi +import android.app.PendingIntent +import android.app.PendingIntent.FLAG_UPDATE_CURRENT +import android.appwidget.AppWidgetManager +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_DELETED +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_OPTIONS +import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT +import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.os.Build +import android.view.View.GONE +import android.view.View.VISIBLE +import android.widget.RemoteViews +import dagger.android.AndroidInjection +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.SharedPrefHelper +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView.MenuView +import io.github.wulkanowy.utils.SchedulersProvider +import io.reactivex.Maybe +import timber.log.Timber +import javax.inject.Inject + +class LuckyNumberWidgetProvider : BroadcastReceiver() { + + @Inject + lateinit var studentRepository: StudentRepository + + @Inject + lateinit var semesterRepository: SemesterRepository + + @Inject + lateinit var luckyNumberRepository: LuckyNumberRepository + + @Inject + lateinit var schedulers: SchedulersProvider + + @Inject + lateinit var appWidgetManager: AppWidgetManager + + @Inject + lateinit var sharedPref: SharedPrefHelper + + companion object { + fun getStudentWidgetKey(appWidgetId: Int) = "lucky_number_widget_student_$appWidgetId" + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + override fun onReceive(context: Context, intent: Intent) { + AndroidInjection.inject(this, context) + when (intent.action) { + ACTION_APPWIDGET_UPDATE -> onUpdate(context, intent) + ACTION_APPWIDGET_DELETED -> onDelete(intent) + ACTION_APPWIDGET_OPTIONS_CHANGED -> onOptionsChange(context, intent) + } + } + + private fun onUpdate(context: Context, intent: Intent) { + intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId -> + RemoteViews(context.packageName, R.layout.widget_luckynumber).apply { + setTextViewText(R.id.luckyNumberWidgetNumber, + getLuckyNumber(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)?.luckyNumber?.toString() ?: "#" + ) + setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, + PendingIntent.getActivity(context, MenuView.LUCKY_NUMBER.id, + MainActivity.getStartIntent(context, MenuView.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT)) + }.also { + setStyles(it, intent) + appWidgetManager.updateAppWidget(appWidgetId, it) + } + } + } + + private fun onDelete(intent: Intent) { + intent.getIntExtra(EXTRA_APPWIDGET_ID, 0).let { + if (it != 0) sharedPref.delete(getStudentWidgetKey(it)) + } + } + + private fun getLuckyNumber(studentId: Long, appWidgetId: Int): LuckyNumber? { + return try { + studentRepository.isStudentSaved() + .filter { true } + .flatMap { studentRepository.getSavedStudents().toMaybe() } + .flatMap { students -> + students.singleOrNull { student -> student.id == studentId } + .let { student -> + when { + student != null -> Maybe.just(student) + studentId != 0L -> { + studentRepository.isCurrentStudentSet() + .filter { true } + .flatMap { studentRepository.getCurrentStudent(false).toMaybe() } + .doOnSuccess { sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) } + } + else -> Maybe.empty() + } + } + } + .flatMap { semesterRepository.getCurrentSemester(it).toMaybe() } + .flatMap { luckyNumberRepository.getLuckyNumber(it) } + .subscribeOn(schedulers.backgroundThread) + .blockingGet() + } catch (e: Exception) { + Timber.e(e, "An error has occurred in lucky number provider") + null + } + } + + private fun onOptionsChange(context: Context, intent: Intent) { + intent.extras?.let { extras -> + RemoteViews(context.packageName, R.layout.widget_luckynumber).apply { + setStyles(this, intent) + appWidgetManager.updateAppWidget(extras.getInt(EXTRA_APPWIDGET_ID), this) + } + } + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + private fun setStyles(views: RemoteViews, intent: Intent) { + val options = intent.extras?.getBundle(EXTRA_APPWIDGET_OPTIONS) + + val maxWidth = options?.getInt(OPTION_APPWIDGET_MAX_WIDTH) ?: 150 + val maxHeight = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: 40 + + Timber.d("New lucky number widget measurement: %dx%d", maxWidth, maxHeight) + + when { + // 1x1 + maxWidth < 150 && maxHeight < 110 -> { + Timber.d("Lucky number widget size: 1x1") + views.run { + setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE) + setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE) + setViewVisibility(R.id.luckyNumberWidgetTitle, GONE) + setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE) + } + } + // 1x2 + maxWidth < 150 && maxHeight > 110 -> { + Timber.d("Lucky number widget size: 1x2") + views.run { + setViewVisibility(R.id.luckyNumberWidgetImageTop, VISIBLE) + setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE) + setViewVisibility(R.id.luckyNumberWidgetTitle, GONE) + setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE) + } + } + // 2x1 + maxWidth >= 150 && maxHeight <= 110 -> { + Timber.d("Lucky number widget size: 2x1") + views.run { + setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE) + setViewVisibility(R.id.luckyNumberWidgetImageLeft, VISIBLE) + setViewVisibility(R.id.luckyNumberWidgetTitle, GONE) + setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE) + } + } + // 2x2 and bigger + else -> { + Timber.d("Lucky number widget size: 2x2 and bigger") + views.run { + setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE) + setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE) + setViewVisibility(R.id.luckyNumberWidgetTitle, VISIBLE) + setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE) + } + } + } + } +} 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 new file mode 100644 index 00000000..779f0a49 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt @@ -0,0 +1,180 @@ +package io.github.wulkanowy.ui.modules.main + +import android.content.Context +import android.content.Intent +import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import androidx.core.content.ContextCompat +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.Fragment +import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.TitleState.ALWAYS_SHOW +import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem +import com.ncapdevi.fragnav.FragNavController +import com.ncapdevi.fragnav.FragNavController.Companion.HIDE +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.modules.account.AccountDialog +import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment +import io.github.wulkanowy.ui.modules.exam.ExamFragment +import io.github.wulkanowy.ui.modules.grade.GradeFragment +import io.github.wulkanowy.ui.modules.homework.HomeworkFragment +import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment +import io.github.wulkanowy.ui.modules.message.MessageFragment +import io.github.wulkanowy.ui.modules.more.MoreFragment +import io.github.wulkanowy.ui.modules.note.NoteFragment +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 + override lateinit var presenter: MainPresenter + + @Inject + lateinit var navController: FragNavController + + companion object { + const val EXTRA_START_MENU = "extraStartMenu" + + fun getStartIntent(context: Context, startMenu: MainView.MenuView? = null, clear: Boolean = false): Intent { + return Intent(context, MainActivity::class.java) + .apply { + if (clear) flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK + startMenu?.let { putExtra(EXTRA_START_MENU, it) } + } + } + } + + 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 var startMenuMoreIndex = -1 + + private val moreMenuFragments = listOf( + MessageFragment.newInstance(), + HomeworkFragment.newInstance(), + NoteFragment.newInstance(), + LuckyNumberFragment.newInstance() + ) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + setSupportActionBar(mainToolbar) + messageContainer = mainFragmentContainer + + presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_START_MENU) as? MainView.MenuView) + + navController.run { + initialize(startMenuIndex, savedInstanceState) + pushFragment(moreMenuFragments.getOrNull(startMenuMoreIndex)) + } + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.action_menu_main, menu) + return true + } + + override fun initView() { + mainBottomNav.run { + addItems( + listOf( + AHBottomNavigationItem(R.string.grade_title, R.drawable.ic_menu_main_grade_26dp, 0), + AHBottomNavigationItem(R.string.attendance_title, R.drawable.ic_menu_main_attendance_24dp, 0), + AHBottomNavigationItem(R.string.exam_title, R.drawable.ic_menu_main_exam_24dp, 0), + AHBottomNavigationItem(R.string.timetable_title, R.drawable.ic_menu_main_timetable_24dp, 0), + AHBottomNavigationItem(R.string.more_title, R.drawable.ic_menu_main_more_24dp, 0) + ) + ) + accentColor = ContextCompat.getColor(context, R.color.colorPrimary) + inactiveColor = getThemeAttrColor(android.R.attr.textColorSecondary) + defaultBackgroundColor = getThemeAttrColor(R.attr.bottomNavBackground) + titleState = ALWAYS_SHOW + currentItem = startMenuIndex + isBehaviorTranslationEnabled = false + setTitleTextSizeInSp(10f, 10f) + setOnTabSelectedListener { position, wasSelected -> + presenter.onTabSelected(position, wasSelected) + } + } + + navController.run { + setOnViewChangeListener { presenter.onViewChange() } + fragmentHideStrategy = HIDE + rootFragments = listOf( + GradeFragment.newInstance(), + AttendanceFragment.newInstance(), + ExamFragment.newInstance(), + TimetableFragment.newInstance(), + MoreFragment.newInstance() + ) + } + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + return if (item?.itemId == R.id.mainMenuAccount) presenter.onAccountManagerSelected() + else false + } + + override fun onSupportNavigateUp(): Boolean { + return presenter.onUpNavigate() + } + + override fun switchMenuView(position: Int) { + navController.switchTab(position) + } + + override fun setViewTitle(title: String) { + supportActionBar?.title = title + } + + override fun showHomeArrow(show: Boolean) { + supportActionBar?.setDisplayHomeAsUpEnabled(show) + } + + override fun showAccountPicker() { + navController.showDialogFragment(AccountDialog.newInstance()) + } + + override fun notifyMenuViewReselected() { + (navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected() + } + + fun showDialogFragment(dialog: DialogFragment) { + navController.showDialogFragment(dialog) + } + + fun pushView(fragment: Fragment) { + navController.pushFragment(fragment) + } + + override fun popView() { + navController.safelyPopFragment() + } + + override fun onBackPressed() { + presenter.onBackPressed { super.onBackPressed() } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + navController.onSaveInstanceState(outState) + intent.removeExtra(EXTRA_START_MENU) + } +} 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 new file mode 100644 index 00000000..cc69005e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt @@ -0,0 +1,111 @@ +package io.github.wulkanowy.ui.modules.main + +import com.ncapdevi.fragnav.FragNavController +import dagger.Module +import dagger.Provides +import dagger.android.ContributesAndroidInjector +import io.github.wulkanowy.R +import io.github.wulkanowy.di.scopes.PerFragment +import io.github.wulkanowy.ui.modules.about.AboutFragment +import io.github.wulkanowy.ui.modules.about.AboutModule +import io.github.wulkanowy.ui.modules.account.AccountDialog +import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment +import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment +import io.github.wulkanowy.ui.modules.exam.ExamFragment +import io.github.wulkanowy.ui.modules.grade.GradeFragment +import io.github.wulkanowy.ui.modules.grade.GradeModule +import io.github.wulkanowy.ui.modules.homework.HomeworkFragment +import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment +import io.github.wulkanowy.ui.modules.message.MessageFragment +import io.github.wulkanowy.ui.modules.message.MessageModule +import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment +import io.github.wulkanowy.ui.modules.mobiledevice.token.MobileDeviceTokenDialog +import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment +import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceModule +import io.github.wulkanowy.ui.modules.more.MoreFragment +import io.github.wulkanowy.ui.modules.note.NoteFragment +import io.github.wulkanowy.ui.modules.settings.SettingsFragment +import io.github.wulkanowy.ui.modules.timetable.TimetableFragment +import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment + +@Module +abstract class MainModule { + + @Module + companion object { + + @JvmStatic + @Provides + fun provideFragNavController(activity: MainActivity): FragNavController { + return FragNavController(activity.supportFragmentManager, R.id.mainFragmentContainer) + } + } + + @PerFragment + @ContributesAndroidInjector + abstract fun bindAttendanceFragment(): AttendanceFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindAttendanceSummaryFragment(): AttendanceSummaryFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindExamFragment(): ExamFragment + + @PerFragment + @ContributesAndroidInjector(modules = [GradeModule::class]) + abstract fun bindGradeFragment(): GradeFragment + + @PerFragment + @ContributesAndroidInjector(modules = [MessageModule::class]) + abstract fun bindMessagesFragment(): MessageFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindMessagePreviewFragment(): MessagePreviewFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindMoreFragment(): MoreFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindTimetableFragment(): TimetableFragment + + @PerFragment + @ContributesAndroidInjector(modules = [AboutModule::class]) + abstract fun bindAboutFragment(): AboutFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindSettingsFragment(): SettingsFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindNoteFragment(): NoteFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindHomeworkFragment(): HomeworkFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindLuckyNumberFragment(): LuckyNumberFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindCompletedLessonsFragment(): CompletedLessonsFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindAccountDialog(): AccountDialog + + @PerFragment + @ContributesAndroidInjector(modules = [MobileDeviceModule::class]) + abstract fun bindMobileDevices(): MobileDeviceFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindMobileDeviceDialog(): MobileDeviceTokenDialog +} 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 new file mode 100644 index 00000000..a74226bf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt @@ -0,0 +1,87 @@ +package io.github.wulkanowy.ui.modules.main + +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.services.sync.SyncManager +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject + +class MainPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val prefRepository: PreferencesRepository, + private val syncManager: SyncManager, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + fun onAttachView(view: MainView, initMenu: MainView.MenuView?) { + super.onAttachView(view) + view.apply { + getProperViewIndexes(initMenu).let { (main, more) -> + startMenuIndex = main + startMenuMoreIndex = more + } + initView() + Timber.i("Main view was initialized with $startMenuIndex menu index and $startMenuMoreIndex more index") + } + + syncManager.startSyncWorker() + analytics.logEvent("app_open", "destination" to initMenu?.name) + } + + fun onViewChange() { + view?.apply { + currentViewTitle?.let { setViewTitle(it) } + currentStackSize?.let { + if (it > 1) showHomeArrow(true) + else showHomeArrow(false) + } + } + } + + fun onAccountManagerSelected(): Boolean { + Timber.i("Select account manager") + view?.showAccountPicker() + return true + } + + fun onUpNavigate(): Boolean { + Timber.i("Up navigate pressed") + view?.popView() + return true + } + + fun onBackPressed(default: () -> Unit) { + Timber.i("Back pressed in main view") + view?.run { + if (isRootView) default() + else popView() + } + } + + fun onTabSelected(index: Int, wasSelected: Boolean): Boolean { + return view?.run { + Timber.i("Switch main tab index: $index, reselected: $wasSelected") + if (wasSelected) { + notifyMenuViewReselected() + false + } else { + switchMenuView(index) + true + } + } == true + } + + private fun getProperViewIndexes(initMenu: MainView.MenuView?): Pair { + return when { + initMenu?.id in 0..3 -> initMenu!!.id to -1 + (initMenu?.id ?: 0) > 3 -> 4 to initMenu!!.id - 4 + else -> prefRepository.startMenuIndex to -1 + } + } +} 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 new file mode 100644 index 00000000..6aef573e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt @@ -0,0 +1,51 @@ +package io.github.wulkanowy.ui.modules.main + +import io.github.wulkanowy.ui.base.BaseView + +interface MainView : BaseView { + + var startMenuIndex: Int + + var startMenuMoreIndex: 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() + + interface MainChildView { + + fun onFragmentReselected() + } + + interface TitledView { + + val titleStringId: Int + } + + enum class MenuView(val id: Int) { + GRADE(0), + ATTENDANCE(1), + EXAM(2), + TIMETABLE(3), + MESSAGE(4), + HOMEWORK(5), + NOTE(6), + LUCKY_NUMBER(7), + } +} 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 new file mode 100644 index 00000000..7bd35f2d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt @@ -0,0 +1,103 @@ +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.db.entities.Message +import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED +import io.github.wulkanowy.data.repositories.message.MessageFolder.SENT +import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity +import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment +import io.github.wulkanowy.utils.setOnSelectPageListener +import kotlinx.android.synthetic.main.fragment_message.* +import javax.inject.Inject + +class MessageFragment : BaseFragment(), MessageView, MainView.TitledView { + + @Inject + lateinit var presenter: MessagePresenter + + @Inject + lateinit var pagerAdapter: BaseFragmentPagerAdapter + + companion object { + fun newInstance() = MessageFragment() + } + + override val titleStringId: Int + get() = R.string.message_title + + override val currentPageIndex: Int + get() = messageViewPager.currentItem + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_message, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this) + } + + override fun initView() { + pagerAdapter.apply { + containerId = messageViewPager.id + addFragmentsWithTitle(mapOf( + MessageTabFragment.newInstance(RECEIVED) to getString(R.string.message_inbox), + MessageTabFragment.newInstance(SENT) to getString(R.string.message_sent), + MessageTabFragment.newInstance(TRASHED) to getString(R.string.message_trash) + )) + } + + messageViewPager.run { + adapter = pagerAdapter + offscreenPageLimit = 2 + setOnSelectPageListener { presenter.onPageSelected(it) } + } + messageTabLayout.setupWithViewPager(messageViewPager) + + openSendMessageButton.setOnClickListener { presenter.onSendMessageButtonClicked() } + } + + override fun showContent(show: Boolean) { + messageViewPager.visibility = if (show) VISIBLE else INVISIBLE + messageTabLayout.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showProgress(show: Boolean) { + messageProgress.visibility = if (show) VISIBLE else INVISIBLE + } + + fun onDeleteMessage(message: Message) { + presenter.onDeleteMessage(message) + } + + fun onChildFragmentLoaded() { + presenter.onChildViewLoaded() + } + + override fun notifyChildMessageDeleted(tabId: Int) { + (pagerAdapter.getFragmentInstance(tabId) as? MessageTabFragment)?.onParentDeleteMessage() + } + + override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) { + (pagerAdapter.getFragmentInstance(index) as? MessageTabFragment)?.onParentLoadData(forceRefresh) + } + + override fun openSendMessage() { + context?.let { it.startActivity(SendMessageActivity.getStartIntent(it)) } + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt new file mode 100644 index 00000000..26568e22 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt @@ -0,0 +1,62 @@ +package io.github.wulkanowy.ui.modules.message + +import android.graphics.Typeface.BOLD +import android.graphics.Typeface.NORMAL +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_message.* + +class MessageItem(val message: Message, private val noSubjectString: String) : + AbstractFlexibleItem() { + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { + return ViewHolder(view, adapter) + } + + override fun getLayoutRes() = R.layout.item_message + + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { + holder.apply { + val style = if (message.unread) BOLD else NORMAL + + messageItemAuthor.run { + text = if (message.recipient.isNotBlank()) message.recipient else message.sender + setTypeface(null, style) + } + messageItemSubject.run { + text = if (message.subject.isNotBlank()) message.subject else noSubjectString + setTypeface(null, style) + } + messageItemDate.run { + text = message.date.toFormattedString() + setTypeface(null, style) + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as MessageItem + + if (message != other.message) return false + return true + } + + override fun hashCode(): Int { + return message.hashCode() + } + + class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageModule.kt new file mode 100644 index 00000000..e4c202d9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageModule.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.ui.modules.message + +import dagger.Module +import dagger.Provides +import dagger.android.ContributesAndroidInjector +import io.github.wulkanowy.di.scopes.PerChildFragment +import io.github.wulkanowy.di.scopes.PerFragment +import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter +import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment + +@Module +abstract class MessageModule { + + @Module + companion object { + + @JvmStatic + @PerFragment + @Provides + fun provideMessageAdapter(fragment: MessageFragment) = BaseFragmentPagerAdapter(fragment.childFragmentManager) + } + + @PerChildFragment + @ContributesAndroidInjector + abstract fun bindMessageTabFragment(): MessageTabFragment +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt new file mode 100644 index 00000000..0f5598b2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt @@ -0,0 +1,61 @@ +package io.github.wulkanowy.ui.modules.message + +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.SchedulersProvider +import io.reactivex.Completable +import timber.log.Timber +import java.util.concurrent.TimeUnit.MILLISECONDS +import javax.inject.Inject + +class MessagePresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: MessageView) { + super.onAttachView(view) + disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread) + .subscribe { + view.initView() + Timber.i("Message view was initialized") + loadData() + }) + } + + fun onPageSelected(index: Int) { + loadChild(index) + } + + private fun loadData() { + view?.run { loadChild(currentPageIndex) } + } + + private fun loadChild(index: Int, forceRefresh: Boolean = false) { + Timber.i("Load message child view index: $index") + view?.notifyChildLoadData(index, forceRefresh) + } + + fun onChildViewLoaded() { + view?.apply { + showContent(true) + showProgress(false) + } + } + + fun onDeleteMessage(message: Message) { + view?.notifyChildMessageDeleted( + when (message.removed) { + true -> 2 + else -> message.folderId - 1 + } + ) + } + + fun onSendMessageButtonClicked() { + view?.openSendMessage() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt new file mode 100644 index 00000000..2aa4d78e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.ui.modules.message + +import io.github.wulkanowy.ui.base.BaseView + +interface MessageView : BaseView { + + val currentPageIndex: Int + + fun initView() + + fun showContent(show: Boolean) + + fun showProgress(show: Boolean) + + fun notifyChildLoadData(index: Int, forceRefresh: Boolean) + + fun notifyChildMessageDeleted(tabId: Int) + + fun openSendMessage() +} 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 new file mode 100644 index 00000000..93ef502f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -0,0 +1,158 @@ +package io.github.wulkanowy.ui.modules.message.preview + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.ui.base.BaseFragment +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.message.send.SendMessageActivity +import kotlinx.android.synthetic.main.fragment_message_preview.* +import javax.inject.Inject + +class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.TitledView { + + @Inject + lateinit var presenter: MessagePreviewPresenter + + private var menuReplyButton: MenuItem? = null + + private var menuForwardButton: MenuItem? = null + + private var menuDeleteButton: MenuItem? = null + + override val titleStringId: Int + get() = R.string.message_title + + override val noSubjectString: String + get() = getString(R.string.message_no_subject) + + override val deleteMessageSuccessString: String + get() = getString(R.string.message_delete_success) + + companion object { + const val MESSAGE_ID_KEY = "message_id" + + fun newInstance(messageId: Long): MessagePreviewFragment { + return MessagePreviewFragment().apply { + arguments = Bundle().apply { putLong(MESSAGE_ID_KEY, messageId) } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_message_preview, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + messageContainer = messagePreviewContainer + presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getLong(MESSAGE_ID_KEY) ?: 0L) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.action_menu_message_preview, menu) + menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply) + menuForwardButton = menu.findItem(R.id.messagePreviewMenuForward) + menuDeleteButton = menu.findItem(R.id.messagePreviewMenuDelete) + presenter.onCreateOptionsMenu() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.messagePreviewMenuReply -> presenter.onReply() + R.id.messagePreviewMenuForward -> presenter.onForward() + R.id.messagePreviewMenuDelete -> presenter.onMessageDelete() + else -> false + } + } + + override fun setSubject(subject: String) { + messagePreviewSubject.text = subject + } + + @SuppressLint("SetTextI18n") + override fun setRecipient(recipient: String) { + messagePreviewAuthor.text = "${getString(R.string.message_to)} $recipient" + } + + @SuppressLint("SetTextI18n") + override fun setSender(sender: String) { + messagePreviewAuthor.text = "${getString(R.string.message_from)} $sender" + } + + override fun setDate(date: String) { + messagePreviewDate.text = getString(R.string.message_date, date) + } + + override fun setContent(content: String) { + messagePreviewContent.text = content + } + + override fun showProgress(show: Boolean) { + messagePreviewProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showContent(show: Boolean) { + messagePreviewContentContainer.visibility = if (show) VISIBLE else GONE + } + + override fun showOptions(show: Boolean) { + menuReplyButton?.isVisible = show + menuForwardButton?.isVisible = show + menuDeleteButton?.isVisible = show + } + + override fun setDeletedOptionsLabels() { + menuDeleteButton?.setTitle(R.string.message_delete_forever) + } + + override fun setNotDeletedOptionsLabels() { + menuDeleteButton?.setTitle(R.string.message_move_to_bin) + } + + override fun showMessageError() { + messagePreviewError.visibility = VISIBLE + } + + override fun openMessageReply(message: Message?) { + context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message, true)) } + } + + override fun openMessageForward(message: Message?) { + context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message)) } + } + + override fun popView() { + (activity as MainActivity).popView() + } + + override fun notifyParentMessageDeleted(message: Message) { + fragmentManager?.fragments?.forEach { if (it is MessageFragment) it.onDeleteMessage(message) } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(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 new file mode 100644 index 00000000..a230c0b3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -0,0 +1,130 @@ +package io.github.wulkanowy.ui.modules.message.preview + +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.repositories.message.MessageRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.toFormattedString +import timber.log.Timber +import javax.inject.Inject + +class MessagePreviewPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val messageRepository: MessageRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + var messageId = 0L + + private var message: Message? = null + + fun onAttachView(view: MessagePreviewView, id: Long) { + super.onAttachView(view) + loadData(id) + } + + private fun loadData(id: Long) { + Timber.i("Loading message $id preview started") + messageId = id + disposable.apply { + clear() + add(studentRepository.getCurrentStudent() + .flatMap { messageRepository.getMessage(it, messageId, true) } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { view?.showProgress(false) } + .subscribe({ message -> + Timber.i("Loading message $id preview result: Success ") + this@MessagePreviewPresenter.message = message + view?.run { + message.let { + setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString) + setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) + setContent(it.content.orEmpty()) + initOptions() + + if (it.recipient.isNotBlank()) setRecipient(it.recipient) + else setSender(it.sender) + } + } + analytics.logEvent("load_message_preview", "length" to message.content?.length) + }) { + Timber.i("Loading message $id preview result: An exception occurred ") + view?.showMessageError() + errorHandler.dispatch(it) + }) + } + } + + fun onReply(): Boolean { + return if (message != null) { + view?.openMessageReply(message) + true + } else false + } + + fun onForward(): Boolean { + return if (message != null) { + view?.openMessageForward(message) + true + } else false + } + + private fun deleteMessage() { + message?.let { message -> + disposable.add(messageRepository.deleteMessage(message) + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doOnSubscribe { + view?.run { + showContent(false) + showProgress(true) + showOptions(false) + } + } + .doFinally { + view?.showProgress(false) + } + .subscribe({ + view?.run { + notifyParentMessageDeleted(message) + showMessage(deleteMessageSuccessString) + popView() + } + }, { error -> + view?.showMessageError() + errorHandler.dispatch(error) + }, { + view?.showMessageError() + }) + ) + } + } + + fun onMessageDelete(): Boolean { + deleteMessage() + return true + } + + private fun initOptions() { + view?.apply { + showOptions(message != null) + message?.let { + when (it.removed) { + true -> setDeletedOptionsLabels() + false -> setNotDeletedOptionsLabels() + } + } + + } + } + + fun onCreateOptionsMenu() { + initOptions() + } +} 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 new file mode 100644 index 00000000..373dee11 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt @@ -0,0 +1,41 @@ +package io.github.wulkanowy.ui.modules.message.preview + +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.ui.base.BaseView + +interface MessagePreviewView : BaseView { + + val noSubjectString: String + + val deleteMessageSuccessString: 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 showContent(show: Boolean) + + fun showOptions(show: Boolean) + + fun setDeletedOptionsLabels() + + fun setNotDeletedOptionsLabels() + + fun showMessageError() + + fun openMessageReply(message: Message?) + + fun openMessageForward(message: Message?) + + fun popView() + + fun notifyParentMessageDeleted(message: Message) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChip.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChip.kt new file mode 100644 index 00000000..be433b00 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChip.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.ui.modules.message.send + +import android.graphics.drawable.Drawable +import android.net.Uri +import com.pchmn.materialchips.model.ChipInterface +import io.github.wulkanowy.data.db.entities.Recipient + +class RecipientChip(var recipient: Recipient) : ChipInterface { + + override fun getAvatarDrawable(): Drawable? = null + + override fun getAvatarUri(): Uri? = null + + override fun getId(): Any = recipient.id + + override fun getLabel(): String = recipient.name + + override fun getInfo(): String { + return recipient.realName.run { + substringBeforeLast("-").let { sub -> + when { + (sub == this) -> this + (sub.indexOf('(') != -1) -> indexOf("(").let { substring(if (it != -1) it else 0) } + (sub.indexOf('[') != -1) -> indexOf("[").let { substring(if (it != -1) it else 0) } + else -> substringAfter('-') + } + }.trim() + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt new file mode 100644 index 00000000..5c6e74b0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt @@ -0,0 +1,130 @@ +package io.github.wulkanowy.ui.modules.message.send + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.view.View.GONE +import android.view.View.VISIBLE +import android.widget.Toast +import android.widget.Toast.LENGTH_LONG +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.showSoftInput +import kotlinx.android.synthetic.main.activity_send_message.* +import javax.inject.Inject + +class SendMessageActivity : BaseActivity(), SendMessageView { + + @Inject + override lateinit var presenter: SendMessagePresenter + + companion object { + private const val EXTRA_MESSAGE = "EXTRA_MESSAGE" + + private const val EXTRA_REPLY = "EXTRA_REPLY" + + fun getStartIntent(context: Context) = Intent(context, SendMessageActivity::class.java) + + fun getStartIntent(context: Context, message: Message?, reply: Boolean = false): Intent { + return getStartIntent(context) + .putExtra(EXTRA_MESSAGE, message) + .putExtra(EXTRA_REPLY, reply) + } + } + + override val formRecipientsData: List + get() = (sendMessageRecipientsInput.selectedChipList).map { (it as RecipientChip).recipient } + + override val formSubjectValue: String + get() = sendMessageSubjectInput.text.toString() + + override val formContentValue: String + get() = sendMessageContentInput.text.toString() + + override val messageRequiredRecipients: String + get() = getString(R.string.message_required_recipients) + + override val messageContentMinLength: String + get() = getString(R.string.message_content_min_length) + + override val messageSuccess: String + get() = getString(R.string.message_send_successful) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_send_message) + setSupportActionBar(sendMessageToolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + messageContainer = sendMessageContainer + + presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, intent.getSerializableExtra(EXTRA_REPLY) as? Boolean) + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.action_menu_send_message, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + return if (item?.itemId == R.id.sendMessageMenuSend) presenter.onSend() + else false + } + + override fun onSupportNavigateUp(): Boolean { + return presenter.onUpNavigate() + } + + override fun setReportingUnit(unit: ReportingUnit) { + sendMessageFromTextView.setText(unit.senderName) + } + + override fun setRecipients(recipients: List) { + sendMessageRecipientsInput.filterableList = recipients.map { RecipientChip(it) } + } + + override fun setSelectedRecipients(recipients: List) { + recipients.map { sendMessageRecipientsInput.addChip(RecipientChip(it)) } + } + + override fun showProgress(show: Boolean) { + sendMessageProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showContent(show: Boolean) { + sendMessageContent.visibility = if (show) VISIBLE else GONE + } + + override fun showEmpty(show: Boolean) { + sendMessageEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showActionBar(show: Boolean) { + supportActionBar?.apply { if (show) show() else hide() } + } + + override fun setSubject(subject: String) { + sendMessageSubjectInput.setText(subject) + } + + override fun setContent(content: String) { + sendMessageContentInput.setText(content) + } + + override fun showMessage(text: String) { + Toast.makeText(this, text, LENGTH_LONG).show() + } + + override fun showSoftInput(show: Boolean) { + if (show) showSoftInput() else hideSoftInput() + } + + override fun popView() { + onBackPressed() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt new file mode 100644 index 00000000..4808fafc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt @@ -0,0 +1,165 @@ +package io.github.wulkanowy.ui.modules.message.send + +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.repositories.message.MessageRepository +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.recipient.RecipientRepository +import io.github.wulkanowy.data.repositories.reportingunit.ReportingUnitRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.toFormattedString +import io.reactivex.Completable +import timber.log.Timber +import javax.inject.Inject + +class SendMessagePresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val messageRepository: MessageRepository, + private val reportingUnitRepository: ReportingUnitRepository, + private val recipientRepository: RecipientRepository, + private val preferencesRepository: PreferencesRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean?) { + super.onAttachView(view) + Timber.i("Send message view was initialized") + loadData(message, reply) + view.apply { + message?.let { + setSubject(when (reply) { + true -> "RE: " + else -> "FW: " + } + message.subject) + if (preferencesRepository.fillMessageContent || reply != true) { + setContent( + when (reply) { + true -> "\n\n" + else -> "" + } + when (message.sender.isNotEmpty()) { + true -> "Od: ${message.sender}\n" + false -> "Do: ${message.recipient}\n" + } + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}") + } + } + } + } + + fun onUpNavigate(): Boolean { + view?.popView() + return true + } + + private fun loadData(message: Message?, reply: Boolean?) { + var reportingUnit: ReportingUnit? = null + var recipients: List = emptyList() + var selectedRecipient: List = emptyList() + + Timber.i("Loading recipients started") + disposable.add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getCurrentSemester(it).map { semester -> it to semester } } + .flatMapCompletable { (student, semester) -> + reportingUnitRepository.getReportingUnit(student, semester.unitId) + .doOnSuccess { reportingUnit = it } + .flatMap { recipientRepository.getRecipients(student, 2, it).toMaybe() } + .doOnSuccess { + Timber.i("Loading recipients result: Success, fetched %d recipients", it.size) + recipients = it + } + .flatMapCompletable { + if (message == null || reply != true) Completable.complete() + else recipientRepository.getMessageRecipients(student, message) + .doOnSuccess { + Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", it.size) + selectedRecipient = it + } + .ignoreElement() + } + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doOnSubscribe { + view?.run { + showProgress(true) + showContent(false) + } + } + .doFinally { view?.run { showProgress(false) } } + .subscribe({ + view?.apply { + if (reportingUnit !== null) { + reportingUnit?.let { setReportingUnit(it) } + setRecipients(recipients) + if (selectedRecipient.isNotEmpty()) setSelectedRecipients(selectedRecipient) + showContent(true) + } else { + Timber.e("Loading recipients result: Can't find the reporting unit") + view?.showEmpty(true) + } + } + }, { + Timber.e("Loading recipients result: An exception occurred") + view?.showContent(true) + errorHandler.dispatch(it) + })) + } + + private fun sendMessage(subject: String, content: String, recipients: List) { + Timber.i("Sending message started") + disposable.add(messageRepository.sendMessage(subject, content, recipients) + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doOnSubscribe { + view?.run { + showSoftInput(false) + showContent(false) + showProgress(true) + showActionBar(false) + } + } + .subscribe({ + Timber.i("Sending message result: Success") + analytics.logEvent("send_message", "recipients" to recipients.size) + view?.run { + showMessage(messageSuccess) + popView() + } + }, { + Timber.i("Sending message result: An exception occurred") + view?.run { + showContent(true) + showProgress(false) + showActionBar(true) + } + errorHandler.dispatch(it) + }) + ) + } + + fun onSend(): Boolean { + view?.run { + when { + formRecipientsData.isEmpty() -> showMessage(messageRequiredRecipients) + formContentValue.length < 3 -> showMessage(messageContentMinLength) + else -> { + sendMessage( + subject = formSubjectValue, + content = formContentValue, + recipients = formRecipientsData + ) + return true + } + } + } + return false + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt new file mode 100644 index 00000000..8a2cb4f6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.ui.modules.message.send + +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.ui.base.BaseView + +interface SendMessageView : BaseView { + + val formRecipientsData: List + + val formSubjectValue: String + + val formContentValue: String + + val messageRequiredRecipients: String + + val messageContentMinLength: String + + val messageSuccess: String + + fun setReportingUnit(unit: ReportingUnit) + + fun setRecipients(recipients: List) + + fun setSelectedRecipients(recipients: List) + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showActionBar(show: Boolean) + + fun setSubject(subject: String) + + fun setContent(content: String) + + fun showSoftInput(show: Boolean) + + fun popView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt new file mode 100644 index 00000000..326cd6a7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt @@ -0,0 +1,134 @@ +package io.github.wulkanowy.ui.modules.message.tab + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import android.view.ViewGroup +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.FlexibleItemDecoration +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.R +import io.github.wulkanowy.data.repositories.message.MessageFolder +import io.github.wulkanowy.ui.base.BaseFragment +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.preview.MessagePreviewFragment +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.fragment_message_tab.* +import javax.inject.Inject + +class MessageTabFragment : BaseFragment(), MessageTabView { + + @Inject + lateinit var presenter: MessageTabPresenter + + @Inject + lateinit var tabAdapter: FlexibleAdapter> + + companion object { + const val MESSAGE_TAB_FOLDER_ID = "message_tab_folder_id" + + fun newInstance(folder: MessageFolder): MessageTabFragment { + return MessageTabFragment().apply { + arguments = Bundle().apply { + putString(MESSAGE_TAB_FOLDER_ID, folder.name) + } + } + } + } + + override val noSubjectString: String + get() = getString(R.string.message_no_subject) + + override val isViewEmpty + get() = tabAdapter.isEmpty + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_message_tab, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + messageContainer = messageTabRecycler + presenter.onAttachView(this, MessageFolder.valueOf( + (savedInstanceState ?: arguments)?.getString(MESSAGE_TAB_FOLDER_ID).orEmpty() + )) + } + + override fun initView() { + tabAdapter.setOnItemClickListener { presenter.onMessageItemSelected(it) } + + messageTabRecycler.run { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = tabAdapter + addItemDecoration(FlexibleItemDecoration(context) + .withDefaultDivider() + .withDrawDividerOnLastItem(false) + ) + } + messageTabSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + } + + override fun updateData(data: List) { + tabAdapter.updateDataSet(data) + } + + override fun updateItem(item: AbstractFlexibleItem<*>) { + tabAdapter.updateItem(item) + } + + override fun clearView() { + tabAdapter.clear() + } + + override fun showProgress(show: Boolean) { + messageTabProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + messageTabSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + messageTabRecycler.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showEmpty(show: Boolean) { + messageTabEmpty.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showRefresh(show: Boolean) { + messageTabSwipe.isRefreshing = show + } + + override fun openMessage(messageId: Long) { + (activity as? MainActivity)?.pushView(MessagePreviewFragment.newInstance(messageId)) + } + + override fun notifyParentDataLoaded() { + (parentFragment as? MessageFragment)?.onChildFragmentLoaded() + } + + fun onParentLoadData(forceRefresh: Boolean) { + presenter.onParentViewLoadData(forceRefresh) + } + + fun onParentDeleteMessage() { + presenter.onDeleteMessage() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putString(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 new file mode 100644 index 00000000..af576293 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -0,0 +1,103 @@ +package io.github.wulkanowy.ui.modules.message.tab + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.repositories.message.MessageFolder +import io.github.wulkanowy.data.repositories.message.MessageRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +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( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val messageRepository: MessageRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + lateinit var folder: MessageFolder + + fun onAttachView(view: MessageTabView, folder: MessageFolder) { + super.onAttachView(view) + view.initView() + this.folder = folder + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the $folder message") + onParentViewLoadData(true) + } + + fun onDeleteMessage() { + loadData(false) + } + + fun onParentViewLoadData(forceRefresh: Boolean) { + loadData(forceRefresh) + } + + fun onMessageItemSelected(item: AbstractFlexibleItem<*>) { + if (item is MessageItem) { + Timber.i("Select message ${item.message.id} item") + view?.run { + openMessage(item.message.id) + if (item.message.unread) { + item.message.unread = false + updateItem(item) + updateMessage(item.message) + } + } + } + } + + private fun loadData(forceRefresh: Boolean) { + Timber.i("Loading $folder message data started") + disposable.apply { + clear() + add(studentRepository.getCurrentStudent() + .flatMap { messageRepository.getMessages(it, folder, forceRefresh) } + .map { items -> items.map { MessageItem(it, view?.noSubjectString.orEmpty()) } } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded() + } + } + .subscribe({ + Timber.i("Loading $folder message result: Success") + view?.run { + showEmpty(it.isEmpty()) + showContent(it.isNotEmpty()) + updateData(it) + } + analytics.logEvent("load_messages", "items" to it.size, "folder" to folder.name) + }) { + Timber.i("Loading $folder message result: An exception occurred") + view?.run { showEmpty(isViewEmpty) } + errorHandler.dispatch(it) + }) + } + } + + private fun updateMessage(message: Message) { + Timber.i("Attempt to update message ${message.id}") + disposable.add(messageRepository.updateMessage(message) + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ Timber.d("Update message ${message.id} result: Success") }) + { error -> + Timber.i("Update message ${message.id} result: An exception occurred") + errorHandler.dispatch(error) + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt new file mode 100644 index 00000000..967863de --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.ui.modules.message.tab + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.ui.base.BaseView +import io.github.wulkanowy.ui.modules.message.MessageItem + +interface MessageTabView : BaseView { + + val noSubjectString: String + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun updateItem(item: AbstractFlexibleItem<*>) + + fun clearView() + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showRefresh(show: Boolean) + + fun openMessage(messageId: Long) + + fun notifyParentDataLoaded() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceAdapter.kt new file mode 100644 index 00000000..518246b6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceAdapter.kt @@ -0,0 +1,10 @@ +package io.github.wulkanowy.ui.modules.mobiledevice + +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.IFlexible +import io.github.wulkanowy.data.db.entities.MobileDevice + +class MobileDeviceAdapter> : FlexibleAdapter(null, null, true) { + + var onDeviceUnregisterListener: (MobileDevice, position: Int) -> Unit = { _, _ -> } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt new file mode 100644 index 00000000..98780e17 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt @@ -0,0 +1,114 @@ +package io.github.wulkanowy.ui.modules.mobiledevice + +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.common.FlexibleItemDecoration +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.helpers.EmptyViewHelper +import eu.davidea.flexibleadapter.helpers.UndoHelper +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.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.mobiledevice.token.MobileDeviceTokenDialog +import kotlinx.android.synthetic.main.fragment_mobile_device.* +import javax.inject.Inject + +class MobileDeviceFragment : BaseFragment(), MobileDeviceView, MainView.TitledView { + + @Inject + lateinit var presenter: MobileDevicePresenter + + @Inject + lateinit var devicesAdapter: MobileDeviceAdapter> + + companion object { + fun newInstance() = MobileDeviceFragment() + } + + override val titleStringId: Int + get() = R.string.mobile_devices_title + + override val isViewEmpty: Boolean + get() = devicesAdapter.isEmpty + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_mobile_device, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + messageContainer = mobileDevicesRecycler + presenter.onAttachView(this) + } + + override fun initView() { + mobileDevicesRecycler.run { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = devicesAdapter + addItemDecoration(FlexibleItemDecoration(context) + .withDefaultDivider() + .withDrawDividerOnLastItem(false) + ) + } + EmptyViewHelper.create(devicesAdapter, mobileDevicesEmpty) + mobileDevicesSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + mobileDeviceAddButton.setOnClickListener { presenter.onRegisterDevice() } + devicesAdapter.run { + isPermanentDelete = false + onDeviceUnregisterListener = { device, position -> + val onActionListener = object : UndoHelper.OnActionListener { + override fun onActionConfirmed(action: Int, event: Int) { + presenter.onUnregister(device) + } + + override fun onActionCanceled(action: Int, positions: MutableList?) { + devicesAdapter.restoreDeletedItems() + } + } + UndoHelper(devicesAdapter, onActionListener) + .withConsecutive(false) + .withAction(UndoHelper.Action.REMOVE) + .start(listOf(position), mobileDevicesRecycler, R.string.mobile_device_removed, R.string.all_undo, 3000) + } + } + } + + override fun updateData(data: List) { + devicesAdapter.updateDataSet(data) + } + + override fun clearData() { + devicesAdapter.clear() + } + + override fun hideRefresh() { + mobileDevicesSwipe.isRefreshing = false + } + + override fun showProgress(show: Boolean) { + mobileDevicesProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + mobileDevicesSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + mobileDevicesRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun showTokenDialog() { + (activity as? MainActivity)?.showDialogFragment(MobileDeviceTokenDialog.newInstance()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceItem.kt new file mode 100644 index 00000000..436c2d0e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceItem.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.ui.modules.mobiledevice + +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.MobileDevice +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_mobile_device.* + +class MobileDeviceItem(val device: MobileDevice) : AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_mobile_device + + 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 { + mobileDeviceItemDate.text = device.date.toFormattedString("dd.MM.yyyy HH:mm:ss") + mobileDeviceItemName.text = device.name + mobileDeviceItemUnregister.setOnClickListener { + (adapter as MobileDeviceAdapter).onDeviceUnregisterListener(device, holder.flexibleAdapterPosition) + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as MobileDeviceItem + + if (device.id != other.device.id) return false + return true + } + + override fun hashCode(): Int { + var result = device.hashCode() + result = 31 * result + device.id.toInt() + return result + } + + class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceModule.kt new file mode 100644 index 00000000..59bbaa9f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceModule.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.ui.modules.mobiledevice + +import dagger.Module +import dagger.Provides +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem + +@Module +class MobileDeviceModule { + + @Provides + fun provideMobileDeviceFlexibleAdapter() = MobileDeviceAdapter>() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt new file mode 100644 index 00000000..47203c8a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt @@ -0,0 +1,94 @@ +package io.github.wulkanowy.ui.modules.mobiledevice + +import io.github.wulkanowy.data.db.entities.MobileDevice +import io.github.wulkanowy.data.repositories.mobiledevice.MobileDeviceRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject + +class MobileDevicePresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val mobileDeviceRepository: MobileDeviceRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: MobileDeviceView) { + super.onAttachView(view) + view.initView() + Timber.i("Mobile device view was initialized") + loadData() + } + + fun onSwipeRefresh() { + loadData(true) + } + + private fun loadData(forceRefresh: Boolean = false) { + Timber.i("Loading mobile devices data started") + disposable.add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { mobileDeviceRepository.getDevices(it, forceRefresh) } + .map { items -> items.map { MobileDeviceItem(it) } } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + } + }.subscribe({ + Timber.i("Loading mobile devices result: Success") + view?.run { + updateData(it) + showContent(it.isNotEmpty()) + } + analytics.logEvent("load_devices", "items" to it.size, "force_refresh" to forceRefresh) + }) { + Timber.i("Loading mobile devices result: An exception occurred") + errorHandler.dispatch(it) + }) + } + + fun onRegisterDevice() { + view?.showTokenDialog() + } + + fun onUnregister(device: MobileDevice) { + Timber.i("Unregister device started") + disposable.add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { semester -> + mobileDeviceRepository.unregisterDevice(semester, device) + .flatMap { mobileDeviceRepository.getDevices(semester, it) } + } + .map { items -> items.map { MobileDeviceItem(it) } } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + showProgress(false) + enableSwipe(true) + } + } + .subscribe({ + Timber.i("Unregister device result: Success") + view?.run { + updateData(it) + showContent(it.isNotEmpty()) + } + }) { + Timber.i("Unregister device result: An exception occurred") + errorHandler.dispatch(it) + } + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt new file mode 100644 index 00000000..ef35cd75 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt @@ -0,0 +1,24 @@ +package io.github.wulkanowy.ui.modules.mobiledevice + +import io.github.wulkanowy.ui.base.BaseView + +interface MobileDeviceView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun hideRefresh() + + fun clearData() + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showTokenDialog() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt new file mode 100644 index 00000000..a4110128 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt @@ -0,0 +1,89 @@ +package io.github.wulkanowy.ui.modules.mobiledevice.token + +import android.graphics.BitmapFactory +import android.os.Bundle +import android.util.Base64 +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.Toast +import dagger.android.support.DaggerDialogFragment +import io.github.wulkanowy.R +import io.github.wulkanowy.data.pojos.MobileDeviceToken +import io.github.wulkanowy.data.repositories.mobiledevice.MobileDeviceRemote +import io.github.wulkanowy.ui.base.BaseActivity +import kotlinx.android.synthetic.main.dialog_mobile_device.* +import javax.inject.Inject + +class MobileDeviceTokenDialog : DaggerDialogFragment(), MobileDeviceTokenVIew { + + @Inject + lateinit var presenter: MobileDeviceTokenPresenter + + companion object { + fun newInstance(): MobileDeviceTokenDialog = MobileDeviceTokenDialog() + } + + 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_mobile_device, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this) + } + + override fun initView() { + mobileDeviceDialogClose.setOnClickListener { dismiss() } + } + + override fun updateData(token: MobileDeviceToken) { + mobileDeviceDialogToken.text = token.token + mobileDeviceDialogSymbol.text = token.symbol + mobileDeviceDialogPin.text = token.pin + + mobileDeviceQr.setImageBitmap(Base64.decode(token.qr, Base64.DEFAULT).let { + BitmapFactory.decodeByteArray(it, 0, it.size) + }) + } + + override fun hideLoading() { + mobileDeviceDialogProgress.visibility = GONE + } + + override fun showContent() { + mobileDeviceDialogContent.visibility = VISIBLE + } + + override fun closeDialog() { + dismiss() + } + + override fun showError(text: String, error: Throwable) { + showMessage(text) + } + + override fun showMessage(text: String) { + Toast.makeText(context, text, Toast.LENGTH_LONG).show() + } + + override fun showExpiredDialog() { + (activity as? BaseActivity<*>)?.showExpiredDialog() + } + + override fun openClearLoginView() { + (activity as? BaseActivity<*>)?.openClearLoginView() + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt new file mode 100644 index 00000000..a778cbed --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt @@ -0,0 +1,51 @@ +package io.github.wulkanowy.ui.modules.mobiledevice.token + +import io.github.wulkanowy.data.repositories.mobiledevice.MobileDeviceRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject + +class MobileDeviceTokenPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val mobileDeviceRepository: MobileDeviceRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: MobileDeviceTokenVIew) { + super.onAttachView(view) + view.initView() + Timber.i("Mobile device view was initialized") + loadData() + } + + private fun loadData() { + Timber.i("Mobile device registration data started") + disposable.add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { mobileDeviceRepository.getToken(it) } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { view?.hideLoading() } + .subscribe({ + Timber.i("Mobile device registration result: Success") + view?.run { + updateData(it) + showContent() + } + analytics.logEvent("device_register", "symbol" to it.token.substring(0, 3)) + }) { + Timber.i("Mobile device registration result: An exception occurred") + view?.closeDialog() + errorHandler.dispatch(it) + } + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenVIew.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenVIew.kt new file mode 100644 index 00000000..950f8bcf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenVIew.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.ui.modules.mobiledevice.token + +import io.github.wulkanowy.data.pojos.MobileDeviceToken +import io.github.wulkanowy.ui.base.BaseView + +interface MobileDeviceTokenVIew : BaseView { + + fun initView() + + fun hideLoading() + + fun showContent() + + fun closeDialog() + + fun updateData(token: MobileDeviceToken) +} 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 new file mode 100644 index 00000000..8b50a1c8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt @@ -0,0 +1,159 @@ +package io.github.wulkanowy.ui.modules.more + +import android.graphics.drawable.Drawable +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.about.AboutFragment +import io.github.wulkanowy.ui.modules.homework.HomeworkFragment +import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.message.MessageFragment +import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment +import io.github.wulkanowy.ui.modules.note.NoteFragment +import io.github.wulkanowy.ui.modules.settings.SettingsFragment +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.fragment_more.* +import javax.inject.Inject + +class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.MainChildView { + + @Inject + lateinit var presenter: MorePresenter + + @Inject + lateinit var moreAdapter: FlexibleAdapter> + + companion object { + fun newInstance() = MoreFragment() + } + + override val titleStringId: Int + get() = R.string.more_title + + + override val messagesRes: Pair? + get() { + return context?.run { + getString(R.string.message_title) to + ContextCompat.getDrawable(this, R.drawable.ic_more_messages_24dp) + } + } + + override val homeworkRes: Pair? + get() { + return context?.run { + getString(R.string.homework_title) to ContextCompat.getDrawable(this, R.drawable.ic_menu_main_homework_24dp) + } + } + + override val noteRes: Pair? + get() { + return context?.run { + getString(R.string.note_title) to ContextCompat.getDrawable(this, R.drawable.ic_menu_main_note_24dp) + } + } + + override val luckyNumberRes: Pair? + get() { + return context?.run { + getString(R.string.lucky_number_title) to + ContextCompat.getDrawable(this, R.drawable.ic_more_lucky_number_24dp) + } + } + + override val mobileDevicesRes: Pair? + get() { + return context?.run { + getString(R.string.mobile_devices_title) to + ContextCompat.getDrawable(this, R.drawable.ic_menu_main_mobile_devices_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() { + if (::presenter.isInitialized) presenter.onViewReselected() + } + + override fun updateData(data: List) { + moreAdapter.updateDataSet(data) + } + + override fun openMessagesView() { + (activity as? MainActivity)?.pushView(MessageFragment.newInstance()) + } + + override fun openHomeworkView() { + (activity as? MainActivity)?.pushView(HomeworkFragment.newInstance()) + } + + override fun openNoteView() { + (activity as? MainActivity)?.pushView(NoteFragment.newInstance()) + } + + override fun openLuckyNumberView() { + (activity as? MainActivity)?.pushView(LuckyNumberFragment.newInstance()) + } + + override fun openMobileDevicesView() { + (activity as? MainActivity)?.pushView(MobileDeviceFragment.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 new file mode 100644 index 00000000..85b604e7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt @@ -0,0 +1,47 @@ +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 new file mode 100644 index 00000000..f9fd5bbf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt @@ -0,0 +1,60 @@ +package io.github.wulkanowy.ui.modules.more + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject + +class MorePresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: MoreView) { + super.onAttachView(view) + view.initView() + Timber.i("More view was initialized") + loadData() + } + + fun onItemSelected(item: AbstractFlexibleItem<*>?) { + if (item is MoreItem) { + Timber.i("Select more item \"${item.title}\"") + view?.run { + when (item.title) { + messagesRes?.first -> openMessagesView() + homeworkRes?.first -> openHomeworkView() + noteRes?.first -> openNoteView() + luckyNumberRes?.first -> openLuckyNumberView() + mobileDevicesRes?.first -> openMobileDevicesView() + settingsRes?.first -> openSettingsView() + aboutRes?.first -> openAboutView() + } + } + } + } + + fun onViewReselected() { + Timber.i("More view is reselected") + view?.popView() + } + + private fun loadData() { + Timber.i("Load items for more view") + view?.run { + updateData(listOfNotNull( + messagesRes?.let { MoreItem(it.first, it.second) }, + homeworkRes?.let { MoreItem(it.first, it.second) }, + noteRes?.let { MoreItem(it.first, it.second) }, + luckyNumberRes?.let { MoreItem(it.first, it.second) }, + mobileDevicesRes?.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 new file mode 100644 index 00000000..9c1abb90 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt @@ -0,0 +1,41 @@ +package io.github.wulkanowy.ui.modules.more + +import android.graphics.drawable.Drawable +import io.github.wulkanowy.ui.base.BaseView + +interface MoreView : BaseView { + + val messagesRes: Pair? + + val homeworkRes: Pair? + + val noteRes: Pair? + + val luckyNumberRes: Pair? + + val mobileDevicesRes: Pair? + + val settingsRes: Pair? + + val aboutRes: Pair? + + fun initView() + + fun updateData(data: List) + + fun openSettingsView() + + fun openAboutView() + + fun popView() + + fun openMessagesView() + + fun openHomeworkView() + + fun openNoteView() + + fun openLuckyNumberView() + + fun openMobileDevicesView() +} 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 new file mode 100644 index 00000000..492aeab2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt @@ -0,0 +1,48 @@ +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 new file mode 100644 index 00000000..6117ae24 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt @@ -0,0 +1,105 @@ +package io.github.wulkanowy.ui.modules.note + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.FlexibleItemDecoration +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.fragment_note.* +import javax.inject.Inject + +class NoteFragment : BaseFragment(), NoteView, MainView.TitledView { + + @Inject + lateinit var presenter: NotePresenter + + @Inject + lateinit var noteAdapter: FlexibleAdapter> + + companion object { + fun newInstance() = NoteFragment() + } + + override val titleStringId: Int + get() = R.string.note_title + + override val isViewEmpty: Boolean + get() = noteAdapter.isEmpty + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_note, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this) + } + + override fun initView() { + noteAdapter.run { + setOnItemClickListener { presenter.onNoteItemSelected(it) } + } + + noteRecycler.run { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = noteAdapter + addItemDecoration(FlexibleItemDecoration(context) + .withDefaultDivider() + .withDrawDividerOnLastItem(false) + ) + } + noteSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + } + + override fun showNoteDialog(note: Note) { + (activity as? MainActivity)?.showDialogFragment(NoteDialog.newInstance(note)) + } + + override fun updateData(data: List) { + noteAdapter.updateDataSet(data, true) + } + + override fun updateItem(item: AbstractFlexibleItem<*>) { + noteAdapter.updateItem(item) + } + + override fun clearData() { + noteAdapter.clear() + } + + override fun showEmpty(show: Boolean) { + noteEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showProgress(show: Boolean) { + noteProgress.visibility = if (show) VISIBLE else GONE + } + + override fun enableSwipe(enable: Boolean) { + noteSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + noteRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun hideRefresh() { + noteSwipe.isRefreshing = false + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt new file mode 100644 index 00000000..dabeef74 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt @@ -0,0 +1,60 @@ +package io.github.wulkanowy.ui.modules.note + +import android.graphics.Typeface.BOLD +import android.graphics.Typeface.NORMAL +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_note.* + +class NoteItem(val note: Note) : AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_note + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): NoteItem.ViewHolder { + return NoteItem.ViewHolder(view, adapter) + } + + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: NoteItem.ViewHolder, position: Int, payloads: MutableList?) { + holder.apply { + noteItemDate.apply { + text = note.date.toFormattedString() + setTypeface(null, if (note.isRead) NORMAL else BOLD) + } + noteItemType.apply { + text = note.category + setTypeface(null, if (note.isRead) NORMAL else BOLD) + } + noteItemTeacher.text = note.teacher + noteItemContent.text = note.content + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as NoteItem + + if (note != other.note) return false + if (note.id != other.note.id) return false + return true + } + + override fun hashCode(): Int { + var result = note.hashCode() + result = 31 * result + note.id.toInt() + return result + } + + class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt new file mode 100644 index 00000000..df98ab88 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt @@ -0,0 +1,92 @@ +package io.github.wulkanowy.ui.modules.note + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.repositories.note.NoteRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject + +class NotePresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val noteRepository: NoteRepository, + private val semesterRepository: SemesterRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: NoteView) { + super.onAttachView(view) + view.initView() + Timber.i("Note view was initialized") + loadData() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the note") + loadData(true) + } + + private fun loadData(forceRefresh: Boolean = false) { + Timber.i("Loading note data started") + disposable.add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getCurrentSemester(it).map { semester -> semester to it } } + .flatMap { noteRepository.getNotes(it.second, it.first, forceRefresh) } + .map { items -> items.map { NoteItem(it) } } + .map { items -> items.sortedByDescending { it.note.date } } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + } + }.subscribe({ + Timber.i("Loading note result: Success") + view?.apply { + updateData(it) + showEmpty(it.isEmpty()) + showContent(it.isNotEmpty()) + } + analytics.logEvent("load_note", "items" to it.size, "force_refresh" to forceRefresh) + }, { + Timber.i("Loading note result: An exception occurred") + view?.run { showEmpty(isViewEmpty) } + errorHandler.dispatch(it) + }) + ) + } + + fun onNoteItemSelected(item: AbstractFlexibleItem<*>?) { + if (item is NoteItem) { + Timber.i("Select note item ${item.note.id}") + view?.run { + showNoteDialog(item.note) + if (!item.note.isRead) { + item.note.isRead = true + updateItem(item) + updateNote(item.note) + } + } + } + } + + private fun updateNote(note: Note) { + Timber.i("Attempt to update note ${note.id}") + disposable.add(noteRepository.updateNote(note) + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ Timber.i("Update note result: Success") }) + { error -> + Timber.i("Update note result: An exception occurred") + errorHandler.dispatch(error) + }) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt new file mode 100644 index 00000000..9a2c12eb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt @@ -0,0 +1,30 @@ +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.BaseView + +interface NoteView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun updateItem(item: AbstractFlexibleItem<*>) + + fun clearData() + + fun showEmpty(show: Boolean) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun hideRefresh() + + fun showNoteDialog(note: Note) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt new file mode 100644 index 00000000..d0550bd4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt @@ -0,0 +1,82 @@ +package io.github.wulkanowy.ui.modules.settings + +import android.content.Context +import android.content.SharedPreferences +import android.os.Bundle +import com.takisoft.preferencex.PreferenceFragmentCompat +import dagger.android.support.AndroidSupportInjection +import io.github.wulkanowy.BuildConfig.DEBUG +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.modules.main.MainView +import javax.inject.Inject + +class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener, + MainView.TitledView, SettingsView { + + @Inject + lateinit var presenter: SettingsPresenter + + companion object { + fun newInstance() = SettingsFragment() + } + + override val titleStringId: Int + get() = R.string.settings_title + + override fun onAttach(context: Context) { + AndroidSupportInjection.inject(this) + super.onAttach(context) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this) + } + + override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.scheme_preferences) + findPreference(getString(R.string.pref_key_notification_debug)).isVisible = DEBUG + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + presenter.onSharedPreferenceChanged(key) + } + + override fun recreateView() { + 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 showExpiredDialog() { + (activity as? BaseActivity<*>)?.showExpiredDialog() + } + + override fun openClearLoginView() { + (activity as? BaseActivity<*>)?.openClearLoginView() + } + + 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 new file mode 100644 index 00000000..a2edc414 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt @@ -0,0 +1,44 @@ +package io.github.wulkanowy.ui.modules.settings + +import com.readystatesoftware.chuck.api.ChuckCollector +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.services.sync.SyncManager +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.isHolidays +import org.threeten.bp.LocalDate.now +import timber.log.Timber +import javax.inject.Inject + +class SettingsPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val preferencesRepository: PreferencesRepository, + private val analytics: FirebaseAnalyticsHelper, + private val syncManager: SyncManager, + private val chuckCollector: ChuckCollector +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: SettingsView) { + super.onAttachView(view) + Timber.i("Settings view was initialized") + view.setServicesSuspended(preferencesRepository.serviceEnableKey, now().isHolidays) + } + + fun onSharedPreferenceChanged(key: String) { + Timber.i("Change settings $key") + preferencesRepository.apply { + when (key) { + serviceEnableKey -> syncManager.run { if (isServiceEnabled) startSyncWorker() else stopSyncWorker() } + servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startSyncWorker(true) + isDebugNotificationEnableKey -> chuckCollector.showNotification(isDebugNotificationEnable) + appThemeKey -> view?.recreateView() + } + } + analytics.logEvent("setting_changed", "name" to key) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt new file mode 100644 index 00000000..1c4bf3b0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt @@ -0,0 +1,10 @@ +package io.github.wulkanowy.ui.modules.settings + +import io.github.wulkanowy.ui.base.BaseView + +interface SettingsView : BaseView { + + fun recreateView() + + 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 new file mode 100644 index 00000000..3e0106d1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.ui.modules.splash + +import android.os.Bundle +import android.widget.Toast +import android.widget.Toast.LENGTH_LONG +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.main.MainActivity +import javax.inject.Inject + +class SplashActivity : BaseActivity(), SplashView { + + @Inject + override lateinit var presenter: SplashPresenter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + presenter.onAttachView(this) + } + + override fun openLoginView() { + startActivity(LoginActivity.getStartIntent(this)) + finish() + } + + override fun openMainView() { + startActivity(MainActivity.getStartIntent(this)) + finish() + } + + override fun showError(text: String, error: Throwable) { + Toast.makeText(this, text, LENGTH_LONG).show() + } +} 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 new file mode 100644 index 00000000..44b53c5f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt @@ -0,0 +1,27 @@ +package io.github.wulkanowy.ui.modules.splash + +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.SchedulersProvider +import javax.inject.Inject + +class SplashPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: SplashView) { + super.onAttachView(view) + disposable.add(studentRepository.isCurrentStudentSet() + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + view.apply { + if (it) openMainView() + else openLoginView() + } + }, { errorHandler.dispatch(it) })) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt new file mode 100644 index 00000000..9efd8123 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt @@ -0,0 +1,10 @@ +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 new file mode 100644 index 00000000..7e286c92 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt @@ -0,0 +1,150 @@ +package io.github.wulkanowy.ui.modules.timetable + +import android.annotation.SuppressLint +import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.android.synthetic.main.dialog_timetable.* +import org.threeten.bp.LocalDateTime + +class TimetableDialog : DialogFragment() { + + private lateinit var lesson: Timetable + + companion object { + private const val ARGUMENT_KEY = "Item" + + fun newInstance(exam: Timetable): TimetableDialog { + return TimetableDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + lesson = getSerializable(ARGUMENT_KEY) as Timetable + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.dialog_timetable, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + + lesson.run { + setInfo(info, teacher, canceled, changes) + setSubject(subject, subjectOld) + setTeacher(teacher, teacherOld) + setGroup(group) + setRoom(room, roomOld) + setTime(start, end) + } + + timetableDialogClose.setOnClickListener { dismiss() } + } + + private fun setSubject(subject: String, subjectOld: String) { + timetableDialogSubject.text = subject + if (subjectOld.isNotBlank() && subjectOld != subject) { + timetableDialogSubject.run { + paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG + text = subjectOld + } + timetableDialogSubjectNew.run { + visibility = VISIBLE + text = subject + } + } + } + + private fun setInfo(info: String, teacher: String, canceled: Boolean, changes: Boolean) { + when { + info.isNotBlank() -> timetableDialogChanges.text = when { + canceled && !changes -> "Lekcja odwołana: $info" + changes && teacher.isNotBlank() -> "Zastępstwo: $teacher" + changes && teacher.isBlank() -> "Zastępstwo, ${info.decapitalize()}" + else -> info.capitalize() + } + else -> { + timetableDialogChangesTitle.visibility = GONE + timetableDialogChanges.visibility = GONE + } + } + } + + private fun setTeacher(teacher: String, teacherOld: String) { + when { + teacherOld.isNotBlank() && teacherOld != teacher -> { + timetableDialogTeacher.run { + visibility = VISIBLE + paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG + text = teacherOld + } + if (teacher.isNotBlank()) { + timetableDialogTeacherNew.run { + visibility = VISIBLE + text = teacher + } + } + } + teacher.isNotBlank() -> timetableDialogTeacher.text = teacher + else -> { + timetableDialogTeacherTitle.visibility = GONE + timetableDialogTeacher.visibility = GONE + } + } + } + + private fun setGroup(group: String) { + group.let { + when { + it.isNotBlank() -> timetableDialogGroup.text = it + else -> { + timetableDialogGroupTitle.visibility = GONE + timetableDialogGroup.visibility = GONE + } + } + } + } + + private fun setRoom(room: String, roomOld: String) { + when { + roomOld.isNotBlank() && roomOld != room -> { + timetableDialogRoom.run { + visibility = VISIBLE + paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG + text = roomOld + } + if (room.isNotBlank()) { + timetableDialogRoomNew.run { + visibility = VISIBLE + text = room + } + } + } + room.isNotBlank() -> timetableDialogRoom.text = room + else -> { + timetableDialogRoomTitle.visibility = GONE + timetableDialogRoom.visibility = GONE + } + } + } + + @SuppressLint("SetTextI18n") + private fun setTime(start: LocalDateTime, end: LocalDateTime) { + timetableDialogTime.text = "${start.toFormattedString("HH:mm")} - ${end.toFormattedString("HH:mm")}" + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt new file mode 100644 index 00000000..e60be74c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt @@ -0,0 +1,161 @@ +package io.github.wulkanowy.ui.modules.timetable + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.FlexibleItemDecoration +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.fragment_timetable.* +import javax.inject.Inject + +class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView, MainView.TitledView { + + @Inject + lateinit var presenter: TimetablePresenter + + @Inject + lateinit var timetableAdapter: FlexibleAdapter> + + companion object { + private const val SAVED_DATE_KEY = "CURRENT_DATE" + + fun newInstance() = TimetableFragment() + } + + override val titleStringId: Int + get() = R.string.timetable_title + + override val roomString: String + get() = getString(R.string.timetable_room) + + override val isViewEmpty: Boolean + get() = timetableAdapter.isEmpty + + override val currentStackSize: Int? + get() = (activity as? MainActivity)?.currentStackSize + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_timetable, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + messageContainer = timetableRecycler + presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) + } + + override fun initView() { + timetableAdapter.run { + setOnItemClickListener { presenter.onTimetableItemSelected(it) } + } + + timetableRecycler.run { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = timetableAdapter + addItemDecoration(FlexibleItemDecoration(context) + .withDefaultDivider() + .withDrawDividerOnLastItem(false) + ) + } + timetableSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + timetablePreviousButton.setOnClickListener { presenter.onPreviousDay() } + timetableNextButton.setOnClickListener { presenter.onNextDay() } + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.action_menu_timetable, menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.timetableMenuCompletedLessons) presenter.onCompletedLessonsSwitchSelected() + else false + } + + override fun updateData(data: List) { + timetableAdapter.updateDataSet(data, true) + } + + override fun clearData() { + timetableAdapter.clear() + } + + override fun updateNavigationDay(date: String) { + timetableNavDate.text = date + } + + override fun hideRefresh() { + timetableSwipe.isRefreshing = false + } + + override fun resetView() { + timetableRecycler.smoothScrollToPosition(0) + } + + override fun onFragmentReselected() { + if (::presenter.isInitialized) presenter.onViewReselected() + } + + override fun popView() { + (activity as? MainActivity)?.popView() + } + + override fun showEmpty(show: Boolean) { + timetableEmpty.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showProgress(show: Boolean) { + timetableProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun enableSwipe(enable: Boolean) { + timetableSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + timetableRecycler.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showPreButton(show: Boolean) { + timetablePreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showNextButton(show: Boolean) { + timetableNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showTimetableDialog(lesson: Timetable) { + (activity as? MainActivity)?.showDialogFragment(TimetableDialog.newInstance(lesson)) + } + + override fun openCompletedLessonsView() { + (activity as? MainActivity)?.pushView(CompletedLessonsFragment.newInstance()) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt new file mode 100644 index 00000000..c721401f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt @@ -0,0 +1,61 @@ +package io.github.wulkanowy.ui.modules.timetable + +import android.annotation.SuppressLint +import android.graphics.Paint +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_timetable.* + +class TimetableItem(val lesson: Timetable, private val roomText: String) : + AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_timetable + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { + return ViewHolder(view, adapter) + } + + @SuppressLint("SetTextI18n") + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { + holder.apply { + timetableItemNumber.text = lesson.number.toString() + timetableItemSubject.text = lesson.subject + timetableItemRoom.text = if (lesson.room.isNotBlank()) "$roomText ${lesson.room}" else "" + timetableItemTime.text = "${lesson.start.toFormattedString("HH:mm")} - ${lesson.end.toFormattedString("HH:mm")}" + timetableItemAlert.visibility = if (lesson.changes || lesson.canceled) VISIBLE else GONE + timetableItemSubject.paintFlags = + if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG + else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as TimetableItem + + if (lesson != other.lesson) return false + return true + } + + override fun hashCode(): Int { + var result = lesson.hashCode() + result = 31 * result + lesson.id.toInt() + return result + } + + class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt new file mode 100644 index 00000000..bead4228 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -0,0 +1,133 @@ +package io.github.wulkanowy.ui.modules.timetable + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.timetable.TimetableRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.isHolidays +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import io.github.wulkanowy.utils.nextSchoolDay +import io.github.wulkanowy.utils.previousSchoolDay +import io.github.wulkanowy.utils.toFormattedString +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDate.now +import org.threeten.bp.LocalDate.ofEpochDay +import timber.log.Timber +import java.util.concurrent.TimeUnit.MILLISECONDS +import javax.inject.Inject + +class TimetablePresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val timetableRepository: TimetableRepository, + private val semesterRepository: SemesterRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + lateinit var currentDate: LocalDate + private set + + fun onAttachView(view: TimetableView, date: Long?) { + super.onAttachView(view) + view.initView() + Timber.i("Timetable was initialized") + loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay())) + reloadView() + } + + fun onPreviousDay() { + loadData(currentDate.previousSchoolDay) + reloadView() + } + + fun onNextDay() { + loadData(currentDate.nextSchoolDay) + reloadView() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the timetable") + loadData(currentDate, true) + } + + fun onViewReselected() { + Timber.i("Timetable view is reselected") + view?.also { view -> + if (view.currentStackSize == 1) { + now().nextOrSameSchoolDay.also { + if (currentDate != it) { + loadData(it) + reloadView() + } else if (!view.isViewEmpty) view.resetView() + } + } else view.popView() + } + } + + fun onTimetableItemSelected(item: AbstractFlexibleItem<*>?) { + if (item is TimetableItem) { + Timber.i("Select exam item ${item.lesson.id}") + view?.showTimetableDialog(item.lesson) + } + } + + fun onCompletedLessonsSwitchSelected(): Boolean { + view?.openCompletedLessonsView() + return true + } + + private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { + Timber.i("Loading timetable data started") + currentDate = date + disposable.apply { + clear() + add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getCurrentSemester(it) } + .delay(200, MILLISECONDS) + .flatMap { timetableRepository.getTimetable(it, currentDate, currentDate, forceRefresh) } + .map { items -> items.map { TimetableItem(it, view?.roomString.orEmpty()) } } + .map { items -> items.sortedBy { it.lesson.number } } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + } + } + .subscribe({ + Timber.i("Loading timetable result: Success") + view?.apply { + updateData(it) + showEmpty(it.isEmpty()) + showContent(it.isNotEmpty()) + } + analytics.logEvent("load_timetable", "items" to it.size, "force_refresh" to forceRefresh) + }) { + Timber.i("Loading timetable result: An exception occurred") + view?.run { showEmpty(isViewEmpty) } + errorHandler.dispatch(it) + }) + } + } + + private fun reloadView() { + Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}") + view?.apply { + showProgress(true) + enableSwipe(false) + showContent(false) + showEmpty(false) + clearData() + showNextButton(!currentDate.plusDays(1).isHolidays) + showPreButton(!currentDate.minusDays(1).isHolidays) + updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize()) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt new file mode 100644 index 00000000..80a2f9b8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt @@ -0,0 +1,43 @@ +package io.github.wulkanowy.ui.modules.timetable + +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.ui.base.BaseView + +interface TimetableView : BaseView { + + val roomString: String + + val isViewEmpty: Boolean + + val currentStackSize: Int? + + fun initView() + + fun updateData(data: List) + + fun updateNavigationDay(date: String) + + fun clearData() + + fun hideRefresh() + + fun resetView() + + fun showEmpty(show: Boolean) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showTimetableDialog(lesson: Timetable) + + fun popView() + + fun openCompletedLessonsView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt new file mode 100644 index 00000000..8f7b1ec5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt @@ -0,0 +1,73 @@ +package io.github.wulkanowy.ui.modules.timetable.completed + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.CompletedLesson +import kotlinx.android.synthetic.main.dialog_lesson_completed.* + +class CompletedLessonDialog : DialogFragment() { + + private lateinit var completedLesson: CompletedLesson + + companion object { + private const val ARGUMENT_KEY = "Item" + + fun newInstance(exam: CompletedLesson): CompletedLessonDialog { + return CompletedLessonDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + completedLesson = getSerializable(CompletedLessonDialog.ARGUMENT_KEY) as CompletedLesson + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.dialog_lesson_completed, container, false) + } + + @SuppressLint("SetTextI18n") + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + + completedLessonDialogSubject.text = completedLesson.subject + completedLessonDialogTopic.text = completedLesson.topic + completedLessonDialogTeacher.text = completedLesson.teacher + completedLessonDialogAbsence.text = completedLesson.absence + completedLessonDialogChanges.text = completedLesson.substitution + completedLessonDialogResources.text = completedLesson.resources + + completedLesson.substitution.let { + if (it.isBlank()) { + completedLessonDialogChangesTitle.visibility = View.GONE + completedLessonDialogChanges.visibility = View.GONE + } else completedLessonDialogChanges.text = it + } + + completedLesson.absence.let { + if (it.isBlank()) { + completedLessonDialogAbsenceTitle.visibility = View.GONE + completedLessonDialogAbsence.visibility = View.GONE + } else completedLessonDialogAbsence.text = it + } + + completedLesson.resources.let { + if (it.isBlank()) { + completedLessonDialogResourcesTitle.visibility = View.GONE + completedLessonDialogResources.visibility = View.GONE + } else completedLessonDialogResources.text = it + } + + completedLessonDialogClose.setOnClickListener { dismiss() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonItem.kt new file mode 100644 index 00000000..716903f5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonItem.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.ui.modules.timetable.completed + +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.CompletedLesson +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_completed_lesson.* + +class CompletedLessonItem(val completedLesson: CompletedLesson) : AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_completed_lesson + + override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): CompletedLessonItem.ViewHolder { + return CompletedLessonItem.ViewHolder(view, adapter) + } + + override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: CompletedLessonItem.ViewHolder?, position: Int, payloads: MutableList?) { + holder?.apply { + completedLessonItemNumber.text = completedLesson.number.toString() + completedLessonItemSubject.text = completedLesson.subject + completedLessonItemTopic.text = completedLesson.topic + completedLessonItemAlert.visibility = if (completedLesson.substitution.isNotEmpty()) VISIBLE else GONE + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as CompletedLessonItem + + if (completedLesson != other.completedLesson) return false + + return true + } + + override fun hashCode(): Int { + return completedLesson.hashCode() + } + + class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : FlexibleViewHolder(view, adapter), + LayoutContainer { + + override val containerView: View? + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt new file mode 100644 index 00000000..ed11607c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt @@ -0,0 +1,27 @@ +package io.github.wulkanowy.ui.modules.timetable.completed + +import android.content.res.Resources +import com.readystatesoftware.chuck.api.ChuckCollector +import io.github.wulkanowy.api.interceptor.FeatureDisabledException +import io.github.wulkanowy.ui.base.ErrorHandler +import javax.inject.Inject + +class CompletedLessonsErrorHandler @Inject constructor( + resources: Resources, + chuckCollector: ChuckCollector +) : ErrorHandler(resources, chuckCollector) { + + var onFeatureDisabled: () -> Unit = {} + + override fun proceed(error: Throwable) { + when (error) { + is FeatureDisabledException -> onFeatureDisabled() + else -> super.proceed(error) + } + } + + override fun clear() { + super.clear() + onFeatureDisabled = {} + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt new file mode 100644 index 00000000..f56652d5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt @@ -0,0 +1,124 @@ +package io.github.wulkanowy.ui.modules.timetable.completed + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.fragment_timetable_completed.* +import javax.inject.Inject + +class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView.TitledView { + + @Inject + lateinit var presenter: CompletedLessonsPresenter + + @Inject + lateinit var completedLessonsAdapter: FlexibleAdapter> + + companion object { + private const val SAVED_DATE_KEY = "CURRENT_DATE" + + fun newInstance() = CompletedLessonsFragment() + } + + override val titleStringId: Int + get() = R.string.completed_lessons_title + + override val isViewEmpty + get() = completedLessonsAdapter.isEmpty + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_timetable_completed, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + messageContainer = completedLessonsRecycler + presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) + } + + override fun initView() { + completedLessonsAdapter.run { + setOnItemClickListener { presenter.onCompletedLessonsItemSelected(it) } + } + + completedLessonsRecycler.run { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = completedLessonsAdapter + } + completedLessonsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + completedLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() } + completedLessonsNextButton.setOnClickListener { presenter.onNextDay() } + } + + override fun updateData(data: List) { + completedLessonsAdapter.updateDataSet(data, true) + } + + override fun clearData() { + completedLessonsAdapter.clear() + } + + override fun updateNavigationDay(date: String) { + completedLessonsNavDate.text = date + } + + override fun hideRefresh() { + completedLessonsSwipe.isRefreshing = false + } + + override fun showEmpty(show: Boolean) { + completedLessonsEmpty.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showFeatureDisabled() { + context?.let { + completedLessonsInfo.text = getString(R.string.error_feature_disabled) + completedLessonsInfoImage.setImageDrawable(ContextCompat.getDrawable(it, R.drawable.ic_all_close_circle_24dp)) + } + } + + override fun showProgress(show: Boolean) { + completedLessonsProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun enableSwipe(enable: Boolean) { + completedLessonsSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + completedLessonsRecycler.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showPreButton(show: Boolean) { + completedLessonsPreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showNextButton(show: Boolean) { + completedLessonsNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showCompletedLessonDialog(completedLesson: CompletedLesson) { + (activity as? MainActivity)?.showDialogFragment(CompletedLessonDialog.newInstance(completedLesson)) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(CompletedLessonsFragment.SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt new file mode 100644 index 00000000..0db5210f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt @@ -0,0 +1,117 @@ +package io.github.wulkanowy.ui.modules.timetable.completed + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.isHolidays +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import io.github.wulkanowy.utils.nextSchoolDay +import io.github.wulkanowy.utils.previousSchoolDay +import io.github.wulkanowy.utils.toFormattedString +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDate.now +import org.threeten.bp.LocalDate.ofEpochDay +import timber.log.Timber +import java.util.concurrent.TimeUnit +import javax.inject.Inject + +class CompletedLessonsPresenter @Inject constructor( + schedulers: SchedulersProvider, + studentRepository: StudentRepository, + private val completedLessonsErrorHandler: CompletedLessonsErrorHandler, + private val semesterRepository: SemesterRepository, + private val completedLessonsRepository: CompletedLessonsRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(completedLessonsErrorHandler, studentRepository, schedulers) { + + lateinit var currentDate: LocalDate + private set + + fun onAttachView(view: CompletedLessonsView, date: Long?) { + super.onAttachView(view) + Timber.i("Completed lessons is attached") + view.initView() + loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay())) + reloadView() + completedLessonsErrorHandler.onFeatureDisabled = { + this.view?.showFeatureDisabled() + Timber.i("Completed lessons feature disabled by school") + } + } + + fun onPreviousDay() { + loadData(currentDate.previousSchoolDay) + reloadView() + } + + fun onNextDay() { + loadData(currentDate.nextSchoolDay) + reloadView() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the completed lessons") + loadData(currentDate, true) + } + + fun onCompletedLessonsItemSelected(item: AbstractFlexibleItem<*>?) { + if (item is CompletedLessonItem) { + Timber.i("Select completed lessons item ${item.completedLesson.id}") + view?.showCompletedLessonDialog(item.completedLesson) + } + } + + private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { + Timber.i("Loading completed lessons data started") + currentDate = date + disposable.apply { + clear() + add(studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getCurrentSemester(it) } + .delay(200, TimeUnit.MILLISECONDS) + .flatMap { completedLessonsRepository.getCompletedLessons(it, currentDate, currentDate, forceRefresh) } + .map { items -> items.map { CompletedLessonItem(it) } } + .map { items -> items.sortedBy { it.completedLesson.number } } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + } + } + .subscribe({ + Timber.i("Loading completed lessons lessons result: Success") + view?.apply { + updateData(it) + showEmpty(it.isEmpty()) + showContent(it.isNotEmpty()) + } + analytics.logEvent("load_completed_lessons", "items" to it.size, "force_refresh" to forceRefresh) + }) { + Timber.i("Loading completed lessons result: An exception occurred") + view?.run { showEmpty(isViewEmpty) } + completedLessonsErrorHandler.dispatch(it) + }) + } + } + + private fun reloadView() { + Timber.i("Reload completed lessons view with the date ${currentDate.toFormattedString()}") + view?.apply { + showProgress(true) + enableSwipe(false) + showContent(false) + showEmpty(false) + clearData() + showNextButton(!currentDate.plusDays(1).isHolidays) + showPreButton(!currentDate.minusDays(1).isHolidays) + updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize()) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt new file mode 100644 index 00000000..a8bef66f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt @@ -0,0 +1,35 @@ +package io.github.wulkanowy.ui.modules.timetable.completed + +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.ui.base.BaseView + +interface CompletedLessonsView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun clearData() + + fun updateNavigationDay(date: String) + + fun hideRefresh() + + fun showEmpty(show: Boolean) + + fun showFeatureDisabled() + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showCompletedLessonDialog(completedLesson: CompletedLesson) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt new file mode 100644 index 00000000..79dd59bf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt @@ -0,0 +1,75 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +import android.widget.Toast.LENGTH_LONG +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.BaseActivity +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_PROVIDER +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.activity_widget_configure.* +import javax.inject.Inject + +class TimetableWidgetConfigureActivity : BaseActivity(), + TimetableWidgetConfigureView { + + @Inject + lateinit var configureAdapter: FlexibleAdapter> + + @Inject + override lateinit var presenter: TimetableWidgetConfigurePresenter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setResult(RESULT_CANCELED) + setContentView(R.layout.activity_widget_configure) + + intent.extras.let { + presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID), it?.getBoolean(EXTRA_FROM_PROVIDER)) + } + } + + override fun initView() { + widgetConfigureRecycler.apply { + adapter = configureAdapter + layoutManager = SmoothScrollLinearLayoutManager(context) + } + configureAdapter.setOnItemClickListener { presenter.onItemSelect(it) } + } + + override fun updateData(data: List) { + configureAdapter.updateDataSet(data) + } + + override fun updateTimetableWidget(widgetId: Int) { + sendBroadcast(Intent(this, TimetableWidgetProvider::class.java) + .apply { + action = ACTION_APPWIDGET_UPDATE + putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId)) + }) + } + + override fun setSuccessResult(widgetId: Int) { + setResult(RESULT_OK, Intent().apply { putExtra(EXTRA_APPWIDGET_ID, widgetId) }) + } + + override fun showError(text: String, error: Throwable) { + Toast.makeText(this, text, LENGTH_LONG).show() + } + + override fun finishView() { + finish() + } + + override fun openLoginView() { + startActivity(LoginActivity.getStartIntent(this)) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt new file mode 100644 index 00000000..f6ec1519 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +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.Student +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_account.* + +class TimetableWidgetConfigureItem(val student: Student, private val isCurrent: Boolean) : + AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_account + + 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 { + accountItemName.text = "${student.studentName} ${student.className}" + accountItemSchool.text = student.schoolName + accountItemImage.setBackgroundResource(if (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 TimetableWidgetConfigureItem + + if (student != other.student) return false + + return true + } + + override fun hashCode(): Int { + var result = student.hashCode() + result = 31 * result + student.id.toInt() + return result + } + + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt new file mode 100644 index 00000000..60fd03aa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt @@ -0,0 +1,65 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.SharedPrefHelper +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey +import io.github.wulkanowy.utils.SchedulersProvider +import javax.inject.Inject + +class TimetableWidgetConfigurePresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val sharedPref: SharedPrefHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + private var appWidgetId: Int? = null + + private var isFromProvider = false + + fun onAttachView(view: TimetableWidgetConfigureView, appWidgetId: Int?, isFromProvider: Boolean?) { + super.onAttachView(view) + this.appWidgetId = appWidgetId + this.isFromProvider = isFromProvider ?: false + view.initView() + loadData() + } + + fun onItemSelect(item: AbstractFlexibleItem<*>) { + if (item is TimetableWidgetConfigureItem) { + registerStudent(item.student) + } + } + + private fun loadData() { + disposable.add(studentRepository.getSavedStudents(false) + .map { it to appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } } + .map { (students, currentStudentId) -> + students.map { student -> TimetableWidgetConfigureItem(student, student.id == currentStudentId) } + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + when { + it.isEmpty() -> view?.openLoginView() + it.size == 1 && !isFromProvider -> registerStudent(it.single().student) + else -> view?.updateData(it) + } + }, { errorHandler.dispatch(it) })) + } + + private fun registerStudent(student: Student) { + appWidgetId?.also { + sharedPref.putLong(getStudentWidgetKey(it), student.id) + view?.apply { + updateTimetableWidget(it) + setSuccessResult(it) + } + } + view?.finishView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt new file mode 100644 index 00000000..98c800d4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import io.github.wulkanowy.ui.base.BaseView + +interface TimetableWidgetConfigureView : BaseView { + + fun initView() + + fun updateData(data: List) + + fun updateTimetableWidget(widgetId: Int) + + fun setSuccessResult(widgetId: Int) + + fun finishView() + + fun openLoginView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt new file mode 100644 index 00000000..b288f7b7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt @@ -0,0 +1,119 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.content.Context +import android.content.Intent +import android.graphics.Paint.ANTI_ALIAS_FLAG +import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG +import android.view.View.GONE +import android.view.View.VISIBLE +import android.widget.AdapterView.INVALID_POSITION +import android.widget.RemoteViews +import android.widget.RemoteViewsService +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.SharedPrefHelper +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.timetable.TimetableRepository +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getDateWidgetKey +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey +import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.toFormattedString +import io.reactivex.Maybe +import org.threeten.bp.LocalDate +import timber.log.Timber + +class TimetableWidgetFactory( + private val timetableRepository: TimetableRepository, + private val studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val sharedPref: SharedPrefHelper, + private val schedulers: SchedulersProvider, + private val context: Context, + private val intent: Intent? +) : RemoteViewsService.RemoteViewsFactory { + + private var lessons = emptyList() + + override fun getLoadingView() = null + + override fun hasStableIds() = true + + override fun getCount() = lessons.size + + override fun getViewTypeCount() = 1 + + override fun getItemId(position: Int) = position.toLong() + + override fun onCreate() {} + + override fun onDestroy() {} + + override fun onDataSetChanged() { + intent?.extras?.getInt(EXTRA_APPWIDGET_ID)?.let { appWidgetId -> + val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0)) + val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0) + + lessons = try { + studentRepository.isStudentSaved() + .filter { true } + .flatMap { studentRepository.getSavedStudents().toMaybe() } + .flatMap { + if (studentId == 0L) throw IllegalArgumentException("Student id is 0") + + it.singleOrNull { student -> student.id == studentId } + .let { student -> + if (student != null) Maybe.just(student) + else Maybe.empty() + } + } + .flatMap { semesterRepository.getCurrentSemester(it).toMaybe() } + .flatMap { timetableRepository.getTimetable(it, date, date).toMaybe() } + .map { item -> item.sortedBy { it.number } } + .subscribeOn(schedulers.backgroundThread) + .blockingGet(emptyList()) + } catch (e: Exception) { + Timber.e(e, "An error has occurred in timetable widget factory") + emptyList() + } + } + } + + override fun getViewAt(position: Int): RemoteViews? { + if (position == INVALID_POSITION || lessons.getOrNull(position) == null) return null + + return RemoteViews(context.packageName, R.layout.item_widget_timetable).apply { + lessons[position].let { + setTextViewText(R.id.timetableWidgetItemSubject, it.subject) + setTextViewText(R.id.timetableWidgetItemNumber, it.number.toString()) + setTextViewText(R.id.timetableWidgetItemTime, it.start.toFormattedString("HH:mm") + + " - ${it.end.toFormattedString("HH:mm")}") + + if (it.room.isNotBlank()) { + setTextViewText(R.id.timetableWidgetItemRoom, "${context.getString(R.string.timetable_room)} ${it.room}") + } else setTextViewText(R.id.timetableWidgetItemRoom, "") + + if (it.info.isNotBlank()) { + setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE) + setTextViewText(R.id.timetableWidgetItemDescription, it.run { + when (true) { + canceled && !changes -> "Lekcja odwołana: $info" + changes && teacher.isNotBlank() -> "Zastępstwo: $teacher" + changes && teacher.isBlank() -> "Zastępstwo, ${info.decapitalize()}" + else -> it.info.capitalize() + } + }) + } else setViewVisibility(R.id.timetableWidgetItemDescription, GONE) + + if (it.canceled) { + setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", + STRIKE_THRU_TEXT_FLAG or ANTI_ALIAS_FLAG) + } else { + setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", ANTI_ALIAS_FLAG) + } + } + setOnClickFillInIntent(R.id.timetableWidgetItemContainer, Intent()) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt new file mode 100644 index 00000000..a462ca7b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt @@ -0,0 +1,180 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import android.app.PendingIntent +import android.app.PendingIntent.FLAG_UPDATE_CURRENT +import android.appwidget.AppWidgetManager +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_DELETED +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.widget.RemoteViews +import dagger.android.AndroidInjection +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.SharedPrefHelper +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.services.widgets.TimetableWidgetService +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView.MenuView +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import io.github.wulkanowy.utils.nextSchoolDay +import io.github.wulkanowy.utils.previousSchoolDay +import io.github.wulkanowy.utils.shortcutWeekDayName +import io.github.wulkanowy.utils.toFormattedString +import io.reactivex.Maybe +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDate.now +import timber.log.Timber +import javax.inject.Inject + +class TimetableWidgetProvider : BroadcastReceiver() { + + @Inject + lateinit var appWidgetManager: AppWidgetManager + + @Inject + lateinit var studentRepository: StudentRepository + + @Inject + lateinit var sharedPref: SharedPrefHelper + + @Inject + lateinit var schedulers: SchedulersProvider + + @Inject + lateinit var analytics: FirebaseAnalyticsHelper + + companion object { + const val EXTRA_FROM_PROVIDER = "extraFromProvider" + + const val EXTRA_TOGGLED_WIDGET_ID = "extraToggledWidget" + + const val EXTRA_BUTTON_TYPE = "extraButtonType" + + const val BUTTON_NEXT = "buttonNext" + + const val BUTTON_PREV = "buttonPrev" + + const val BUTTON_RESET = "buttonReset" + + fun getDateWidgetKey(appWidgetId: Int) = "timetable_widget_date_$appWidgetId" + + fun getStudentWidgetKey(appWidgetId: Int) = "timetable_widget_student_$appWidgetId" + } + + override fun onReceive(context: Context, intent: Intent) { + AndroidInjection.inject(this, context) + when (intent.action) { + ACTION_APPWIDGET_UPDATE -> onUpdate(context, intent) + ACTION_APPWIDGET_DELETED -> onDelete(intent) + } + } + + private fun onUpdate(context: Context, intent: Intent) { + if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) { + intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId -> + val student = getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId) + updateWidget(context, appWidgetId, now().nextOrSameSchoolDay, student) + } + } else { + val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE) + val toggledWidgetId = intent.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0) + val student = getStudent(sharedPref.getLong(getStudentWidgetKey(toggledWidgetId), 0), toggledWidgetId) + val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0)) + val date = when (buttonType) { + BUTTON_RESET -> now().nextOrSameSchoolDay + BUTTON_NEXT -> savedDate.nextSchoolDay + BUTTON_PREV -> savedDate.previousSchoolDay + else -> now().nextOrSameSchoolDay + } + if (!buttonType.isNullOrBlank()) analytics.logEvent("changed_timetable_widget_day", "button" to buttonType) + updateWidget(context, toggledWidgetId, date, student) + } + } + + private fun onDelete(intent: Intent) { + intent.getIntExtra(EXTRA_APPWIDGET_ID, 0).let { + if (it != 0) { + sharedPref.apply { + delete(getStudentWidgetKey(it)) + delete(getDateWidgetKey(it)) + } + } + } + } + + private fun updateWidget(context: Context, appWidgetId: Int, date: LocalDate, student: Student?) { + RemoteViews(context.packageName, R.layout.widget_timetable).apply { + setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty) + setTextViewText(R.id.timetableWidgetDate, "${date.shortcutWeekDayName.capitalize()} ${date.toFormattedString()}") + setTextViewText(R.id.timetableWidgetName, student?.studentName ?: context.getString(R.string.all_no_data)) + setRemoteAdapter(R.id.timetableWidgetList, Intent(context, TimetableWidgetService::class.java) + .apply { putExtra(EXTRA_APPWIDGET_ID, appWidgetId) }) + setOnClickPendingIntent(R.id.timetableWidgetNext, createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT)) + setOnClickPendingIntent(R.id.timetableWidgetPrev, createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV)) + createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET).let { + setOnClickPendingIntent(R.id.timetableWidgetDate, it) + setOnClickPendingIntent(R.id.timetableWidgetName, it) + } + setOnClickPendingIntent(R.id.timetableWidgetAccount, PendingIntent.getActivity(context, -Int.MAX_VALUE + appWidgetId, + Intent(context, TimetableWidgetConfigureActivity::class.java).apply { + addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK) + putExtra(EXTRA_APPWIDGET_ID, appWidgetId) + putExtra(EXTRA_FROM_PROVIDER, true) + }, FLAG_UPDATE_CURRENT)) + setPendingIntentTemplate(R.id.timetableWidgetList, + PendingIntent.getActivity(context, MenuView.TIMETABLE.id, + MainActivity.getStartIntent(context, MenuView.TIMETABLE, true), FLAG_UPDATE_CURRENT)) + }.also { + sharedPref.putLong(getDateWidgetKey(appWidgetId), date.toEpochDay(), true) + appWidgetManager.apply { + notifyAppWidgetViewDataChanged(appWidgetId, R.id.timetableWidgetList) + updateAppWidget(appWidgetId, it) + } + } + } + + private fun createNavIntent(context: Context, code: Int, appWidgetId: Int, buttonType: String): PendingIntent { + return PendingIntent.getBroadcast(context, code, + Intent(context, TimetableWidgetProvider::class.java).apply { + action = ACTION_APPWIDGET_UPDATE + putExtra(EXTRA_BUTTON_TYPE, buttonType) + putExtra(EXTRA_TOGGLED_WIDGET_ID, appWidgetId) + }, FLAG_UPDATE_CURRENT) + } + + private fun getStudent(studentId: Long, appWidgetId: Int): Student? { + return try { + studentRepository.isStudentSaved() + .filter { true } + .flatMap { studentRepository.getSavedStudents(false).toMaybe() } + .flatMap { students -> + students.singleOrNull { student -> student.id == studentId } + .let { student -> + when { + student != null -> Maybe.just(student) + studentId != 0L -> { + studentRepository.isCurrentStudentSet() + .filter { true } + .flatMap { studentRepository.getCurrentStudent(false).toMaybe() } + .doOnSuccess { sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) } + } + else -> Maybe.empty() + } + } + } + .subscribeOn(schedulers.backgroundThread) + .blockingGet() + } catch (e: Exception) { + Timber.e(e, "An error has occurred in timetable widget provider") + null + } + } +} 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 deleted file mode 100644 index 21ce8ba5..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashActivity.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.splash; - -import android.os.Bundle; -import android.support.v7.app.AppCompatDelegate; - -import javax.inject.Inject; - -import io.github.wulkanowy.services.notifies.NotificationService; -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); - presenter.attachView(this); - } - - @Override - protected void onDestroy() { - presenter.detachView(); - super.onDestroy(); - } - - @Override - public void openLoginActivity() { - startActivity(LoginActivity.getStartIntent(this)); - finish(); - } - - @Override - public void openMainActivity() { - startActivity(MainActivity.getStartIntent(this)); - finish(); - } - - public void setCurrentThemeMode(int mode) { - AppCompatDelegate.setDefaultNightMode(mode); - } - - @Override - public void cancelNotifications() { - new NotificationService(getApplicationContext()).cancelAll(); - } -} 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 deleted file mode 100644 index a1c5ad72..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashContract.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.ui.splash; - -import io.github.wulkanowy.ui.base.BaseContract; - -public interface SplashContract { - - interface View extends BaseContract.View { - - void openLoginActivity(); - - void openMainActivity(); - - void cancelNotifications(); - - void setCurrentThemeMode(int mode); - } - - interface Presenter extends BaseContract.Presenter { - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashModule.java b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashModule.java deleted file mode 100644 index 838b411d..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashModule.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.ui.splash; - -import dagger.Binds; -import dagger.Module; -import io.github.wulkanowy.di.scopes.PerActivity; - -@Module -public abstract class SplashModule { - - @PerActivity - @Binds - abstract SplashContract.Presenter provideSplashPresenter(SplashPresenter splashPresenter); -} 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 deleted file mode 100644 index 69423348..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.java +++ /dev/null @@ -1,30 +0,0 @@ -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 attachView(@NonNull SplashContract.View view) { - super.attachView(view); - getView().setCurrentThemeMode(getRepository().getSharedRepo().getCurrentTheme()); - getView().cancelNotifications(); - - if (getRepository().getSharedRepo().isUserLoggedIn()) { - getView().openMainActivity(); - } else { - getView().openLoginActivity(); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetFactory.java b/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetFactory.java deleted file mode 100644 index 9e632396..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetFactory.java +++ /dev/null @@ -1,140 +0,0 @@ -package io.github.wulkanowy.ui.widgets; - -import android.content.Context; -import android.content.Intent; -import android.graphics.Paint; -import android.view.View; -import android.widget.AdapterView; -import android.widget.RemoteViews; -import android.widget.RemoteViewsService; - -import org.apache.commons.lang3.StringUtils; - -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; -import io.github.wulkanowy.data.db.dao.entities.Week; - -import static io.github.wulkanowy.utils.TimeUtilsKt.getFirstDayOfCurrentWeek; -import static io.github.wulkanowy.utils.TimeUtilsKt.getTodayOrNextDayOrder; - -public class TimetableWidgetFactory implements RemoteViewsService.RemoteViewsFactory { - - private final Context context; - - private List lessonList = new ArrayList<>(); - - private final RepositoryContract repository; - - public TimetableWidgetFactory(Context context, RepositoryContract repository) { - this.context = context; - this.repository = repository; - } - - - @Override - public void onCreate() { - // do nothing - } - - @Override - public void onDataSetChanged() { - lessonList = new ArrayList<>(); - - if (repository.getSharedRepo().isUserLoggedIn()) { - - Week week = repository.getDbRepo().getWeek(getFirstDayOfCurrentWeek()); - int valueOfDay = getTodayOrNextDayOrder(repository.getSharedRepo().getTimetableWidgetState()); - - if (valueOfDay != 5 && valueOfDay != 6 && week != null) { - week.resetDayList(); - lessonList = week.getDayList().get(valueOfDay).getTimetableLessons(); - } - } - } - - @Override - public void onDestroy() { - // do nothing - } - - @Override - public int getCount() { - return lessonList.size(); - } - - @Override - public RemoteViews getViewAt(int position) { - if (position == AdapterView.INVALID_POSITION) { - return null; - } - - RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.timetable_widget_item); - views.setTextViewText(R.id.timetable_widget_item_subject, getSubjectName(position)); - views.setTextViewText(R.id.timetable_widget_item_time, getTimeText(position)); - views.setTextViewText(R.id.timetable_widget_item_room, getRoomText(position)); - - if (!getDescriptionText(position).isEmpty()) { - views.setViewVisibility(R.id.timetable_widget_item_description, View.VISIBLE); - views.setTextViewText(R.id.timetable_widget_item_description, getDescriptionText(position)); - } else { - views.setViewVisibility(R.id.timetable_widget_item_description, View.GONE); - } - - if (lessonList.get(position).getMovedOrCanceled()) { - views.setInt(R.id.timetable_widget_item_subject, "setPaintFlags", - Paint.STRIKE_THRU_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG); - } else { - views.setInt(R.id.timetable_widget_item_subject, "setPaintFlags", - Paint.ANTI_ALIAS_FLAG); - } - - views.setOnClickFillInIntent(R.id.timetable_widget_item_container, new Intent()); - return views; - } - - @Override - public RemoteViews getLoadingView() { - return null; - } - - @Override - public int getViewTypeCount() { - return 1; - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public boolean hasStableIds() { - return true; - } - - private String getSubjectName(int position) { - return lessonList.get(position).getSubject(); - } - - private String getRoomText(int position) { - TimetableLesson lesson = lessonList.get(position); - if (!lesson.getRoom().isEmpty()) { - return context.getString(R.string.timetable_dialog_room) + " " + lesson.getRoom(); - } - return lesson.getRoom(); - } - - private String getTimeText(int position) { - TimetableLesson lesson = lessonList.get(position); - return lesson.getStartTime() + " - " + lesson.getEndTime(); - } - - private String getDescriptionText(int position) { - return StringUtils.capitalize(lessonList.get(position).getDescription()); - } - -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetProvider.java b/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetProvider.java deleted file mode 100644 index aa164e29..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetProvider.java +++ /dev/null @@ -1,112 +0,0 @@ -package io.github.wulkanowy.ui.widgets; - -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProvider; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.support.v4.app.TaskStackBuilder; -import android.widget.RemoteViews; - -import javax.inject.Inject; - -import dagger.android.AndroidInjection; -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.RepositoryContract; -import io.github.wulkanowy.services.widgets.TimetableWidgetServices; -import io.github.wulkanowy.ui.main.MainActivity; - -import static io.github.wulkanowy.utils.TimeUtilsKt.getTodayOrNextDay; - -public class TimetableWidgetProvider extends AppWidgetProvider { - - private static final String ACTION_TIMETABLE_TOGGLE = "timetable_toggle"; - - @Inject - RepositoryContract repository; - - @Override - public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { - inject(context); - - for (int appWidgetId : appWidgetIds) { - RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.timetable_widget); - - setViews(views, context, appWidgetId); - setToggleIntent(views, context); - setTemplateIntent(views, context); - updateWidget(views, appWidgetManager, appWidgetId); - } - super.onUpdate(context, appWidgetManager, appWidgetIds); - } - - @Override - public void onReceive(final Context context, Intent intent) { - super.onReceive(context, intent); - inject(context); - - if (ACTION_TIMETABLE_TOGGLE.equals(intent.getAction())) { - AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); - ComponentName thisWidget = new ComponentName(context.getPackageName(), - TimetableWidgetProvider.class.getName()); - int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget); - - repository.getSharedRepo().setTimetableWidgetState(!repository.getSharedRepo().getTimetableWidgetState()); - onUpdate(context, appWidgetManager, appWidgetIds); - } - } - - @Override - public void onDisabled(Context context) { - super.onDisabled(context); - inject(context); - repository.getSharedRepo().setTimetableWidgetState(false); - } - - private void setToggleIntent(RemoteViews views, Context context) { - Intent refreshIntent = new Intent(context, TimetableWidgetProvider.class); - refreshIntent.setAction(ACTION_TIMETABLE_TOGGLE); - PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, - refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT); - views.setOnClickPendingIntent(R.id.timetable_widget_toggle, pendingIntent); - } - - private void setTemplateIntent(RemoteViews views, Context context) { - Intent intent = MainActivity.getStartIntent(context); - intent.putExtra(MainActivity.EXTRA_CARD_ID_KEY, 3); - - PendingIntent pendingIntent = TaskStackBuilder.create(context) - .addNextIntentWithParentStack(intent) - .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); - - views.setPendingIntentTemplate(R.id.timetable_widget_list, pendingIntent); - } - - private void setViews(RemoteViews views, Context context, int appWidgetId) { - Intent intent = new Intent(context, TimetableWidgetServices.class); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); - - views.setRemoteAdapter(appWidgetId, R.id.timetable_widget_list, intent); - views.setEmptyView(R.id.timetable_widget_list, R.id.timetable_widget_empty); - - boolean nextDay = repository.getSharedRepo().getTimetableWidgetState(); - - String toggleText = context.getString(nextDay ? R.string.widget_timetable_tomorrow - : R.string.widget_timetable_today); - - views.setTextViewText(R.id.timetable_widget_toggle, toggleText); - views.setTextViewText(R.id.timetable_widget_date, getTodayOrNextDay(nextDay)); - } - - private void updateWidget(RemoteViews views, AppWidgetManager appWidgetManager, int appWidgetId) { - appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.timetable_widget_list); - appWidgetManager.updateAppWidget(appWidgetId, views); - } - - private void inject(Context context) { - if (repository == null) { - AndroidInjection.inject(this, context); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ActivityExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ActivityExtension.kt new file mode 100644 index 00000000..93781a9f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/ActivityExtension.kt @@ -0,0 +1,17 @@ +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/AnimationUtils.java b/app/src/main/java/io/github/wulkanowy/utils/AnimationUtils.java deleted file mode 100644 index 04116423..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/AnimationUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.utils; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.view.View; - -public final class AnimationUtils { - - public static void slideDown(final View view) { - view.setVisibility(View.VISIBLE); - view.setAlpha(0.f); - - view.setTranslationY(-(view.getHeight() / 2)); - view.animate() - .translationY(0) - .alpha(1.f) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - view.setVisibility(View.VISIBLE); - view.setAlpha(1.f); - } - }); - } - - public static void slideUp(final View view) { - view.animate() - .translationY(-(view.getHeight() / 2)) - .alpha(0.f) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - // superfluous restoration - view.setVisibility(View.GONE); - view.setAlpha(1.f); - view.setTranslationY(0.f); - } - }); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/AppConstant.java b/app/src/main/java/io/github/wulkanowy/utils/AppConstant.java deleted file mode 100644 index 2499974d..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/AppConstant.java +++ /dev/null @@ -1,27 +0,0 @@ -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"; - - public static final String REPO_URL = "https://github.com/wulkanowy/wulkanowy"; - - 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 new file mode 100644 index 00000000..c2b1efaa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt @@ -0,0 +1,26 @@ +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/CommonUtils.java b/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java deleted file mode 100644 index 9c7b35fb..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.wulkanowy.utils; - -import android.app.Activity; -import android.content.Context; -import android.content.res.TypedArray; -import android.net.Uri; -import android.support.annotation.AttrRes; -import android.support.annotation.ColorInt; -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; - } - } - - @ColorInt - public static int getThemeAttrColor(Context context, @AttrRes int colorAttr) { - final TypedArray array = context.obtainStyledAttributes(null, new int[]{colorAttr}); - try { - return array.getColor(0, 0); - } finally { - array.recycle(); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt new file mode 100644 index 00000000..c367ecd0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt @@ -0,0 +1,28 @@ +package io.github.wulkanowy.utils + +import android.content.Context +import android.content.Intent +import androidx.annotation.AttrRes +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.core.content.ContextCompat + +@ColorInt +fun Context.getThemeAttrColor(@AttrRes colorAttr: Int): Int { + val array = obtainStyledAttributes(null, intArrayOf(colorAttr)) + return try { + array.getColor(0, 0) + } finally { + array.recycle() + } +} + +@ColorInt +fun Context.getCompatColor(@ColorRes colorRes: Int) = ContextCompat.getColor(this, colorRes) + +fun Context.openInternetBrowser(uri: String, onActivityNotFound: (uri: String) -> Unit) { + Intent.parseUri(uri, 0).let { + if (it.resolveActivity(packageManager) != null) startActivity(it) + else onActivityNotFound(uri) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java deleted file mode 100644 index cd4490d6..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java +++ /dev/null @@ -1,202 +0,0 @@ -package io.github.wulkanowy.utils; - - -import java.util.ArrayList; -import java.util.List; - -import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; -import io.github.wulkanowy.data.db.dao.entities.Day; -import io.github.wulkanowy.data.db.dao.entities.Diary; -import io.github.wulkanowy.data.db.dao.entities.Exam; -import io.github.wulkanowy.data.db.dao.entities.Grade; -import io.github.wulkanowy.data.db.dao.entities.School; -import io.github.wulkanowy.data.db.dao.entities.Semester; -import io.github.wulkanowy.data.db.dao.entities.Student; -import io.github.wulkanowy.data.db.dao.entities.Subject; -import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; -import io.github.wulkanowy.data.db.dao.entities.Week; - -public final class DataObjectConverter { - - private DataObjectConverter() { - throw new IllegalStateException("Utility class"); - } - - public static List schoolsToSchoolsEntities(List schools, Long symbolId) { - List studentList = new ArrayList<>(); - - for (io.github.wulkanowy.api.generic.School school : schools) { - studentList.add(new School() - .setName(school.getName()) - .setCurrent(school.getCurrent()) - .setRealId(school.getId()) - .setSymbolId(symbolId) - ); - } - - return studentList; - } - - public static List studentsToStudentEntities(List students, Long schoolId) { - List studentList = new ArrayList<>(); - - for (io.github.wulkanowy.api.generic.Student student : students) { - studentList.add(new Student() - .setName(student.getName()) - .setCurrent(student.isCurrent()) - .setRealId(student.getId()) - .setSchoolId(schoolId) - ); - } - - return studentList; - } - - public static List diariesToDiaryEntities(List diaryList, Long studentId) { - List diaryEntityList = new ArrayList<>(); - - for (io.github.wulkanowy.api.generic.Diary diary : diaryList) { - diaryEntityList.add(new Diary() - .setStudentId(studentId) - .setValue(diary.getId()) - .setName(diary.getName()) - .setCurrent(diary.isCurrent())); - } - - return diaryEntityList; - } - - public static List semestersToSemesterEntities(List semesters, long diaryId) { - List semesterList = new ArrayList<>(); - - for (io.github.wulkanowy.api.generic.Semester semester : semesters) { - semesterList.add(new Semester() - .setDiaryId(diaryId) - .setName(semester.getName()) - .setCurrent(semester.isCurrent()) - .setValue(semester.getId()) - ); - } - - return semesterList; - } - - public static List subjectsToSubjectEntities(List subjectList, long semesterId) { - List subjectEntityList = new ArrayList<>(); - - for (io.github.wulkanowy.api.grades.Subject subject : subjectList) { - Subject subjectEntity = new Subject() - .setSemesterId(semesterId) - .setName(subject.getName()) - .setPredictedRating(subject.getPredictedRating()) - .setFinalRating(subject.getFinalRating()); - subjectEntityList.add(subjectEntity); - } - - return subjectEntityList; - } - - public static List gradesToGradeEntities(List gradeList, long semesterId) { - List gradeEntityList = new ArrayList<>(); - - for (io.github.wulkanowy.api.grades.Grade grade : gradeList) { - gradeEntityList.add(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()) - .setSemesterId(semesterId)); - } - - 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()); - } - - public static Day timetableDayToDayEntity(io.github.wulkanowy.api.timetable.TimetableDay 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 List lessonsToTimetableLessonsEntities(List lessonList) { - List lessonEntityList = new ArrayList<>(); - - for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) { - lessonEntityList.add(new TimetableLesson() - .setNumber(lesson.getNumber()) - .setSubject(lesson.getSubject()) - .setTeacher(lesson.getTeacher()) - .setRoom(lesson.getRoom()) - .setDescription(lesson.getDescription()) - .setGroup(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())); - } - - return lessonEntityList; - } - - public static List lessonsToAttendanceLessonsEntities(List lessonList) { - List lessonEntityList = new ArrayList<>(); - - for (io.github.wulkanowy.api.generic.Lesson lesson : lessonList) { - lessonEntityList.add(new AttendanceLesson() - .setNumber(lesson.getNumber()) - .setSubject(lesson.getSubject()) - .setDate(lesson.getDate()) - .setPresence(lesson.isPresence()) - .setAbsenceUnexcused(lesson.isAbsenceUnexcused()) - .setAbsenceExcused(lesson.isAbsenceExcused()) - .setUnexcusedLateness(lesson.isUnexcusedLateness()) - .setAbsenceForSchoolReasons(lesson.isAbsenceForSchoolReasons()) - .setExcusedLateness(lesson.isExcusedLateness()) - .setExemption(lesson.isExemption())); - } - return lessonEntityList; - } - - public static List examsToExamsEntity(List examList) { - List examEntityList = new ArrayList<>(); - - for (io.github.wulkanowy.api.exams.Exam exam : examList) { - examEntityList.add(new Exam() - .setDescription(exam.getDescription()) - .setEntryDate(exam.getEntryDate()) - .setSubjectAndGroup(exam.getSubjectAndGroup()) - .setTeacher(exam.getTeacher()) - .setType(exam.getType())); - } - return examEntityList; - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/EditTextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/EditTextExtension.kt new file mode 100644 index 00000000..caa977ec --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/EditTextExtension.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.utils + +import android.text.Editable +import android.text.TextWatcher +import android.widget.EditText + +inline fun EditText.setOnTextChangedListener(crossinline listener: () -> Unit) { + addTextChangedListener(object : TextWatcher { + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + listener() + } + + override fun afterTextChanged(s: Editable?) {} + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} + }) +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/EntitiesCompare.java b/app/src/main/java/io/github/wulkanowy/utils/EntitiesCompare.java deleted file mode 100644 index 5681bad1..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/EntitiesCompare.java +++ /dev/null @@ -1,42 +0,0 @@ -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/FabricUtils.java b/app/src/main/java/io/github/wulkanowy/utils/FabricUtils.java deleted file mode 100644 index 69897caa..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/FabricUtils.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.utils; - -import com.crashlytics.android.answers.Answers; -import com.crashlytics.android.answers.CustomEvent; -import com.crashlytics.android.answers.LoginEvent; -import com.crashlytics.android.answers.SignUpEvent; - -public final class FabricUtils { - - private FabricUtils() { - throw new IllegalStateException("Utility class"); - } - - public static void logLogin(String method, boolean result) { - Answers.getInstance().logLogin(new LoginEvent() - .putMethod(method) - .putSuccess(result) - ); - } - - public static void logRegister(boolean result, String symbol, String message) { - Answers.getInstance().logSignUp(new SignUpEvent() - .putMethod("Login activity") - .putSuccess(result) - .putCustomAttribute("symbol", symbol) - .putCustomAttribute("message", message) - ); - } - - public static void logRefresh(String name, boolean result, String date) { - Answers.getInstance().logCustom( - new CustomEvent(name + " refresh") - .putCustomAttribute("Success", result ? "true" : "false") - .putCustomAttribute("Date", date) - ); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/FlexibleAdapterExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/FlexibleAdapterExtension.kt new file mode 100644 index 00000000..bd6867a3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/FlexibleAdapterExtension.kt @@ -0,0 +1,11 @@ +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 new file mode 100644 index 00000000..dcf47754 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt @@ -0,0 +1,20 @@ +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 new file mode 100644 index 00000000..c863b030 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt @@ -0,0 +1,74 @@ +package io.github.wulkanowy.utils + +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.GradeSummary + +fun List.calcAverage(): Double { + var counter = 0.0 + var denominator = 0.0 + + forEach { + counter += (it.value + it.modifier) * it.weightValue + denominator += it.weightValue + } + return if (denominator != 0.0) counter / denominator else 0.0 +} + +@JvmName("calcSummaryAverage") +fun List.calcAverage(): Double { + return asSequence().mapNotNull { + if (it.finalGrade.matches("[0-6]".toRegex())) it.finalGrade.toDouble() else null + }.average().let { if (it.isNaN()) 0.0 else it } +} + +fun Grade.getBackgroundColor(theme: String): Int { + return when (theme) { + "grade_color" -> when (color) { + "000000" -> R.color.grade_black + "F04C4C" -> R.color.grade_red + "20A4F7" -> R.color.grade_blue + "6ECD07" -> R.color.grade_green + "B16CF1" -> R.color.grade_purple + else -> R.color.grade_material_default + } + "material" -> when (value) { + 6 -> R.color.grade_material_six + 5 -> R.color.grade_material_five + 4 -> R.color.grade_material_four + 3 -> R.color.grade_material_three + 2 -> R.color.grade_material_two + 1 -> R.color.grade_material_one + else -> R.color.grade_material_default + } + else -> when (value) { + 6 -> R.color.grade_vulcan_six + 5 -> R.color.grade_vulcan_five + 4 -> R.color.grade_vulcan_four + 3 -> R.color.grade_vulcan_three + 2 -> R.color.grade_vulcan_two + 1 -> R.color.grade_vulcan_one + else -> R.color.grade_vulcan_default + } + } +} + +inline val Grade.colorStringId: Int + get() { + return when (color) { + "000000" -> R.string.all_black + "F04C4C" -> R.string.all_red + "20A4F7" -> R.string.all_blue + "6ECD07" -> R.string.all_green + "B16CF1" -> R.string.all_purple + else -> R.string.all_empty_color + } + } + +fun Grade.changeModifier(plusModifier: Double, minusModifier: Double): Grade { + return when { + modifier != .0 && plusModifier != .0 && modifier > 0 -> copy(modifier = plusModifier) + modifier != .0 && minusModifier != .0 && modifier < 0 -> copy(modifier = -minusModifier) + else -> this + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/GradeUtils.java b/app/src/main/java/io/github/wulkanowy/utils/GradeUtils.java deleted file mode 100644 index 7b6ba193..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/GradeUtils.java +++ /dev/null @@ -1,172 +0,0 @@ -package io.github.wulkanowy.utils; - -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import io.github.wulkanowy.R; -import io.github.wulkanowy.data.db.dao.entities.Grade; -import io.github.wulkanowy.data.db.dao.entities.Subject; - -public final class GradeUtils { - - private final static Pattern validGradePattern = Pattern.compile("^(\\++|-|--|=)?[0-6](\\++|-|--|=)?$"); - private final static Pattern simpleGradeValuePattern = Pattern.compile("([0-6])"); - - private GradeUtils() { - throw new IllegalStateException("Utility class"); - } - - public static float calculateWeightedAverage(List gradeList) { - - float counter = 0f; - float denominator = 0f; - - for (Grade grade : gradeList) { - int weight = getWeightValue(grade.getWeight()); - float value = getWeightedGradeValue(grade.getValue()); - - if (value != -1.0f) { - counter += value * weight; - denominator += weight; - } - } - - if (counter == 0f) { - return -1.0f; - } - return counter / denominator; - } - - public static float calculateSubjectsAverage(List subjectList, boolean usePredicted) { - return calculateSubjectsAverage(subjectList, usePredicted, false); - } - - public static float calculateDetailedSubjectsAverage(List subjectList) { - return calculateSubjectsAverage(subjectList, false, true); - } - - public static int getValueColor(String value) { - Matcher m1 = validGradePattern.matcher(value); - if (!m1.find()) { - return R.color.grade_default; - } - - Matcher m2 = simpleGradeValuePattern.matcher(m1.group()); - if (!m2.find()) { - return R.color.grade_default; - } - - switch (Integer.parseInt(m2.group())) { - case 6: - return R.color.grade_six; - case 5: - return R.color.grade_five; - case 4: - return R.color.grade_four; - case 3: - return R.color.grade_three; - case 2: - return R.color.grade_two; - case 1: - return R.color.grade_one; - default: - return R.color.grade_default; - } - } - - private static float calculateSubjectsAverage(List subjectList, boolean usePredicted, boolean useSubjectsAverages) { - float counter = 0f; - float denominator = 0f; - - for (Subject subject : subjectList) { - float value; - - if (useSubjectsAverages) { - value = calculateWeightedAverage(subject.getGradeList()); - } else { - value = getGradeValue(usePredicted ? subject.getPredictedRating() : subject.getFinalRating()); - } - - if (value != -1.0f) { - counter += Math.round(value); - denominator++; - } - } - - if (counter == 0) { - return -1.0f; - } - - return counter / denominator; - } - - public static float getGradeValue(String grade) { - if (validGradePattern.matcher(grade).matches()) { - return getWeightedGradeValue(grade); - } - - return getVerbalGradeValue(grade); - } - - private static float getVerbalGradeValue(String grade) { - switch (grade) { - case "celujący": - return 6f; - case "bardzo dobry": - return 5f; - case "dobry": - return 4f; - case "dostateczny": - return 3f; - case "dopuszczający": - return 2f; - case "niedostateczny": - return 1f; - default: - return -1f; - } - } - - public static String getShortGradeValue(String grade) { - switch (grade) { - case "celujący": - return "6"; - case "bardzo dobry": - return "5"; - case "dobry": - return "4"; - case "dostateczny": - return "3"; - case "dopuszczający": - return "2"; - case "niedostateczny": - return "1"; - default: - return grade; - } - } - - private static float getWeightedGradeValue(String value) { - if (validGradePattern.matcher(value).matches()) { - if (value.matches("[-][0-6]") || value.matches("[0-6][-]")) { - String replacedValue = value.replaceAll("[-]", ""); - return Float.valueOf(replacedValue) - 0.33f; - } else if (value.matches("[+][0-6]") || value.matches("[0-6][+]")) { - String replacedValue = value.replaceAll("[+]", ""); - return Float.valueOf((replacedValue)) + 0.33f; - } else if (value.matches("[-|=]{1,2}[0-6]") || value.matches("[0-6][-|=]{1,2}")) { - String replacedValue = value.replaceAll("[-|=]{1,2}", ""); - return Float.valueOf((replacedValue)) - 0.5f; - } else { - return Float.valueOf(value); - } - } else { - return -1; - } - } - - private static int getWeightValue(String weightOfGrade) { - return Integer.valueOf(weightOfGrade.substring(0, weightOfGrade.length() - 3)); - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/KeyboardUtils.java b/app/src/main/java/io/github/wulkanowy/utils/KeyboardUtils.java deleted file mode 100644 index 6be1b76f..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/KeyboardUtils.java +++ /dev/null @@ -1,33 +0,0 @@ -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 new file mode 100644 index 00000000..d21b998b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/LibsBuiderExtension.kt @@ -0,0 +1,16 @@ +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/ListExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ListExtension.kt new file mode 100644 index 00000000..4374aeb4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/ListExtension.kt @@ -0,0 +1,9 @@ +package io.github.wulkanowy.utils + +infix fun List.uniqueSubtract(other: List): List { + val list = toMutableList() + other.forEach { + list.remove(it) + } + return list.toList() +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.java b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.java deleted file mode 100644 index 6e6c701b..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.wulkanowy.utils; - -import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; - -import com.crashlytics.android.Crashlytics; - -import timber.log.Timber; - -public final class LoggerUtils { - - public static class CrashlyticsTree extends Timber.Tree { - - @Override - protected void log(int priority, @Nullable String tag, @Nullable String message, @Nullable Throwable t) { - Crashlytics.setInt("priority", priority); - Crashlytics.setString("tag", tag); - - if (t == null) { - Crashlytics.log(message); - } else { - Crashlytics.setString("message", message); - Crashlytics.logException(t); - } - } - } - - public static class DebugLogTree extends Timber.DebugTree { - - @Override - protected void log(int priority, String tag, @NonNull String message, Throwable t) { - if ("HUAWEI".equals(Build.MANUFACTURER) || "samsung".equals(Build.MANUFACTURER)) { - if (priority == Log.VERBOSE || priority == Log.DEBUG || priority == Log.INFO) { - priority = Log.ERROR; - } - } - super.log(priority, AppConstant.APP_NAME, message, t); - } - - @Override - protected String createStackElementTag(@NonNull StackTraceElement element) { - return super.createStackElementTag(element) + " - " + element.getLineNumber(); - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt new file mode 100644 index 00000000..f9db0465 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt @@ -0,0 +1,104 @@ +package io.github.wulkanowy.utils + +import android.app.Activity +import android.app.Application +import android.content.Context +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Singleton + +class DebugLogTree : Timber.DebugTree() { + + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + super.log(priority, "Wulkanowy", message, t) + } +} + +class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks { + + override fun onActivityPaused(activity: Activity?) { + activity?.let { Timber.d("${it::class.java.simpleName} PAUSED") } + } + + override fun onActivityResumed(activity: Activity?) { + activity?.let { Timber.d("${it::class.java.simpleName} RESUMED") } + } + + override fun onActivityStarted(activity: Activity?) { + activity?.let { Timber.d("${it::class.java.simpleName} STARTED") } + } + + override fun onActivityDestroyed(activity: Activity?) { + activity?.let { Timber.d("${it::class.java.simpleName} DESTROYED") } + } + + override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) { + activity?.let { Timber.d("${it::class.java.simpleName} SAVED INSTANCE STATE") } + } + + override fun onActivityStopped(activity: Activity?) { + activity?.let { Timber.d("${it::class.java.simpleName} STOPPED") } + } + + override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { + activity?.let { Timber.d("${it::class.java.simpleName} CREATED ${checkSavedState(savedInstanceState)}") } + } +} + +@Singleton +class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLifecycleCallbacks() { + + override fun onFragmentViewCreated(fm: FragmentManager, f: Fragment, v: View, savedInstanceState: Bundle?) { + Timber.d("${f::class.java.simpleName} VIEW CREATED ${checkSavedState(savedInstanceState)}") + } + + override fun onFragmentStopped(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} STOPPED") + } + + override fun onFragmentCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) { + Timber.d("${f::class.java.simpleName} CREATED ${checkSavedState(savedInstanceState)}") + } + + override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} RESUMED") + } + + override fun onFragmentAttached(fm: FragmentManager, f: Fragment, context: Context) { + Timber.d("${f::class.java.simpleName} ATTACHED") + } + + override fun onFragmentDestroyed(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} DESTROYED") + } + + override fun onFragmentSaveInstanceState(fm: FragmentManager, f: Fragment, outState: Bundle) { + Timber.d("${f::class.java.simpleName} SAVED INSTANCE STATE") + } + + override fun onFragmentStarted(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} STARTED") + } + + override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} VIEW DESTROYED") + } + + override fun onFragmentActivityCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) { + Timber.d("${f::class.java.simpleName} ACTIVITY CREATED ${checkSavedState(savedInstanceState)}") + } + + override fun onFragmentPaused(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} PAUSED") + } + + override fun onFragmentDetached(fm: FragmentManager, f: Fragment) { + Timber.d("${f::class.java.simpleName} DETACHED") + } +} + +private fun checkSavedState(savedInstanceState: Bundle?) = if (savedInstanceState == null) "(STATE IS NULL)" else "" diff --git a/app/src/main/java/io/github/wulkanowy/utils/NetworkUtils.java b/app/src/main/java/io/github/wulkanowy/utils/NetworkUtils.java deleted file mode 100644 index a1484eb9..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/NetworkUtils.java +++ /dev/null @@ -1,18 +0,0 @@ -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/SchedulersProvider.kt b/app/src/main/java/io/github/wulkanowy/utils/SchedulersProvider.kt new file mode 100644 index 00000000..e426d4d5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/SchedulersProvider.kt @@ -0,0 +1,14 @@ +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 new file mode 100644 index 00000000..d9e34e87 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/SpinnerExtension.kt @@ -0,0 +1,22 @@ +package io.github.wulkanowy.utils + +import android.view.View +import android.widget.AdapterView +import android.widget.Spinner + +/** + * @see How to keep onItemSelected from firing off on a newly instantiated Spinner? + */ +inline fun Spinner.setOnItemSelectedListener(crossinline listener: (view: View?) -> Unit) { + onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) {} + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) {} + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + listener(view) + } + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt new file mode 100644 index 00000000..426816f4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt @@ -0,0 +1,127 @@ +package io.github.wulkanowy.utils + +import org.threeten.bp.DateTimeUtils +import org.threeten.bp.DayOfWeek.FRIDAY +import org.threeten.bp.DayOfWeek.MONDAY +import org.threeten.bp.DayOfWeek.SATURDAY +import org.threeten.bp.DayOfWeek.SUNDAY +import org.threeten.bp.Instant +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDateTime +import org.threeten.bp.Month +import org.threeten.bp.ZoneId +import org.threeten.bp.format.DateTimeFormatter +import org.threeten.bp.format.DateTimeFormatter.ofPattern +import org.threeten.bp.format.TextStyle.FULL_STANDALONE +import org.threeten.bp.temporal.TemporalAdjusters.firstInMonth +import org.threeten.bp.temporal.TemporalAdjusters.next +import org.threeten.bp.temporal.TemporalAdjusters.previous +import java.util.Date +import java.util.Locale + +private const val DATE_PATTERN = "dd.MM.yyyy" + +fun Date.toLocalDate(): LocalDate { + return Instant.ofEpochMilli(this.time).atZone(ZoneId.systemDefault()).toLocalDate() +} + +fun Date.toLocalDateTime(): LocalDateTime { + return Instant.ofEpochMilli(this.time).atZone(ZoneId.systemDefault()).toLocalDateTime() +} + +fun String.toLocalDate(format: String = DATE_PATTERN): LocalDate { + return LocalDate.parse(this, DateTimeFormatter.ofPattern(format)) +} + +fun LocalDate.toFormattedString(format: String = DATE_PATTERN): String = this.format(ofPattern(format)) + +fun LocalDateTime.toFormattedString(format: String = DATE_PATTERN): String = this.format(ofPattern(format)) + +fun LocalDateTime.toDate(): Date = DateTimeUtils.toDate(atZone(ZoneId.systemDefault()).toInstant()) + +/** + * https://github.com/ThreeTen/threetenbp/issues/55 + */ + +fun Month.getFormattedName(): String { + return getDisplayName(FULL_STANDALONE, Locale.getDefault()) + .let { + when (it) { + "stycznia" -> "Styczeń" + "lutego" -> "Luty" + "marca" -> "Marzec" + "kwietnia" -> "Kwiecień" + "maja" -> "Maj" + "czerwca" -> "Czerwiec" + "lipca" -> "Lipiec" + "sierpnia" -> "Sierpień" + "września" -> "Wrzesień" + "października" -> "Październik" + "listopada" -> "Listopad" + "grudnia" -> "Grudzień" + else -> it + } + } +} + +inline val LocalDate.nextSchoolDay: LocalDate + get() { + return when (this.dayOfWeek) { + FRIDAY, SATURDAY, SUNDAY -> this.with(next(MONDAY)) + else -> this.plusDays(1) + } + } + +inline val LocalDate.previousSchoolDay: LocalDate + get() { + return when (this.dayOfWeek) { + SATURDAY, SUNDAY, MONDAY -> this.with(previous(FRIDAY)) + else -> this.minusDays(1) + } + } + +inline val LocalDate.nextOrSameSchoolDay: LocalDate + get() { + return when (this.dayOfWeek) { + SATURDAY, SUNDAY -> this.with(next(MONDAY)) + else -> this + } + } + +inline val LocalDate.previousOrSameSchoolDay: LocalDate + get() { + return when (this.dayOfWeek) { + SATURDAY, SUNDAY -> this.with(previous(FRIDAY)) + else -> this + } + } + +inline val LocalDate.weekDayName: String + get() = this.format(ofPattern("EEEE", Locale.getDefault())) + +inline val LocalDate.shortcutWeekDayName: String + get() = this.format(ofPattern("EEE", 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.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt deleted file mode 100644 index c61c7e0d..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt +++ /dev/null @@ -1,97 +0,0 @@ -package io.github.wulkanowy.utils - -import org.threeten.bp.DayOfWeek.* -import org.threeten.bp.LocalDate -import org.threeten.bp.Year -import org.threeten.bp.format.DateTimeFormatter -import org.threeten.bp.temporal.TemporalAdjusters -import java.util.* - -private val formatter = DateTimeFormatter.ofPattern(AppConstant.DATE_PATTERN) - -fun getParsedDate(dateString: String, dateFormat: String): LocalDate { - return LocalDate.parse(dateString, DateTimeFormatter.ofPattern(dateFormat)) -} - -fun getMondaysFromCurrentSchoolYear() = getMondaysFromCurrentSchoolYear(LocalDate.now()) - -fun getMondaysFromCurrentSchoolYear(date: LocalDate): List { - val startDate = getFirstSchoolDay(getSchoolYearForDate(date)) - ?.with(TemporalAdjusters.previousOrSame(MONDAY)) - val endDate = getFirstSchoolDay(getSchoolYearForDate(date) + 1) - ?.with(TemporalAdjusters.previousOrSame(MONDAY)) - - val dateList = ArrayList() - var monday = startDate as LocalDate - while (monday.isBefore(endDate)) { - dateList.add(monday.format(formatter)) - monday = monday.plusWeeks(1) - } - - return dateList -} - -fun getSchoolYearForDate(date: LocalDate): Int { - return if (date.monthValue <= 8) date.year - 1 else date.year -} - -fun getFirstDayOfCurrentWeek(): String = getFirstDayOfCurrentWeek(LocalDate.now()) - -fun getFirstDayOfCurrentWeek(date: LocalDate): String { - return when (date.dayOfWeek) { - SATURDAY -> date.plusDays(2) - SUNDAY -> date.plusDays(1) - else -> date.with(MONDAY) - }.format(formatter) -} - -fun getTodayOrNextDayOrder(next: Boolean): Int = getTodayOrNextDayOrder(next, LocalDate.now()) - -fun getTodayOrNextDayOrder(next: Boolean, date: LocalDate): Int { - val day = date.dayOfWeek - return if (next) { - if (day == SUNDAY) { - 0 - } else day.value - } else day.value - 1 -} - -fun getTodayOrNextDay(next: Boolean): String? = getTodayOrNextDay(next, LocalDate.now()) - -fun getTodayOrNextDay(next: Boolean, date: LocalDate): String? { - return (if (next) { - date.plusDays(1) - } else date).format(formatter) -} - -fun isDateInWeek(firstWeekDay: LocalDate, date: LocalDate): Boolean { - return date.isAfter(firstWeekDay.minusDays(1)) && date.isBefore(firstWeekDay.plusDays(5)) -} - -/** - * [Dz.U. 2016 poz. 1335](http://prawo.sejm.gov.pl/isap.nsf/DocDetails.xsp?id=WDU20160001335) - */ -fun isHolidays(): Boolean = isHolidays(LocalDate.now(), Year.now().value) - -fun isHolidays(day: LocalDate, year: Int): Boolean { - return day.isAfter(getLastSchoolDay(year)) && day.isBefore(getFirstSchoolDay(year)) -} - -fun getFirstSchoolDay(year: Int): LocalDate? { - val firstSeptember = LocalDate.of(year, 9, 1) - - return when (firstSeptember.dayOfWeek) { - FRIDAY, - SATURDAY, - SUNDAY -> firstSeptember.with(TemporalAdjusters.firstInMonth(MONDAY)) - else -> { - firstSeptember - } - } -} - -fun getLastSchoolDay(year: Int): LocalDate? { - return LocalDate - .of(year, 6, 20) - .with(TemporalAdjusters.next(FRIDAY)) -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt new file mode 100644 index 00000000..6a5ad880 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt @@ -0,0 +1,13 @@ +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 deleted file mode 100644 index 44721416..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.github.wulkanowy.utils.async; - -import android.os.AsyncTask; - -import timber.log.Timber; - -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 { - Timber.e("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 { - Timber.e("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 { - Timber.e("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 deleted file mode 100644 index 0d33c57c..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/async/AsyncListeners.java +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index 1ee4a9fa..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/security/CryptoException.java +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 8e425a89..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java +++ /dev/null @@ -1,176 +0,0 @@ -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 timber.log.Timber; - -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); - } - return new String(Base64.encode(plainText.getBytes(), Base64.DEFAULT)); - } - - 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); - } - 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"); - } - - Timber.d("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 new file mode 100644 index 00000000..24e6d3ff --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt @@ -0,0 +1,149 @@ +@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 new file mode 100644 index 00000000..59f830fa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/security/ScramblerException.kt @@ -0,0 +1,6 @@ +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/contact-email.txt similarity index 100% rename from app/src/main/play/contactEmail rename to app/src/main/play/contact-email.txt diff --git a/app/src/main/play/contactPhone b/app/src/main/play/contact-phone.txt similarity index 100% rename from app/src/main/play/contactPhone rename to app/src/main/play/contact-phone.txt diff --git a/app/src/main/play/contactWebsite b/app/src/main/play/contact-website.txt similarity index 100% rename from app/src/main/play/contactWebsite rename to app/src/main/play/contact-website.txt diff --git a/app/src/main/play/defaultLanguage b/app/src/main/play/default-language.txt similarity index 100% rename from app/src/main/play/defaultLanguage rename to app/src/main/play/default-language.txt diff --git a/app/src/main/play/pl-PL/listing/fulldescription b/app/src/main/play/listings/pl-PL/full-description.txt similarity index 51% rename from app/src/main/play/pl-PL/listing/fulldescription rename to app/src/main/play/listings/pl-PL/full-description.txt index 4af6238a..d33cb56f 100644 --- a/app/src/main/play/pl-PL/listing/fulldescription +++ b/app/src/main/play/listings/pl-PL/full-description.txt @@ -1,10 +1,14 @@ Aplikacja jest we wczesnej fazie rozwoju, ciągle pracujemy nad kolejnymi funkcjami. Wyróżnione cechy i funkcje: -- Całkowicie darmowa i otwarta (brak jakichkolwiek reklam i mikropłatności) -- Powiadomienia -- Tryb offline -- Aktywne wsparcie i rozwój +- obliczanie średniej ważonej, +- procentowy podgląd frekwencji, +- szczęśliwy numerek, +- podgląd lekcji zrealizowanych, +- ciemny motyw. +- brak reklam, +- tryb offline, +- powiadomienia. GitHub: https://github.com/wulkanowy/wulkanowy Discord: https://discord.gg/vccAQBr diff --git a/app/src/main/play/listings/pl-PL/graphics/feature-graphic/feature.png b/app/src/main/play/listings/pl-PL/graphics/feature-graphic/feature.png new file mode 100644 index 00000000..094b25b6 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/feature-graphic/feature.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/icon/icon.png b/app/src/main/play/listings/pl-PL/graphics/icon/icon.png new file mode 100644 index 00000000..8c33c2f9 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/icon/icon.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/account-switcher.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/account-switcher.png new file mode 100644 index 00000000..8a52d8de Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/account-switcher.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-dialog.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-dialog.png new file mode 100644 index 00000000..c6508574 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-dialog.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-statistics.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-statistics.png new file mode 100644 index 00000000..376fc3c3 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-statistics.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/grades.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/grades.png new file mode 100644 index 00000000..6b4089b0 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/grades.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/more.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/more.png new file mode 100644 index 00000000..c3dc523f Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/more.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-dialog.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-dialog.png new file mode 100644 index 00000000..511433d4 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-dialog.png differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-widget.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-widget.png new file mode 100644 index 00000000..1d502470 Binary files /dev/null and b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-widget.png differ diff --git a/app/src/main/play/listings/pl-PL/short-description.txt b/app/src/main/play/listings/pl-PL/short-description.txt new file mode 100644 index 00000000..c850c159 --- /dev/null +++ b/app/src/main/play/listings/pl-PL/short-description.txt @@ -0,0 +1 @@ +Nieoficjalna aplikacja ucznia i rodzica dla dziennika VULCAN UONET+ diff --git a/app/src/main/play/listings/pl-PL/title.txt b/app/src/main/play/listings/pl-PL/title.txt new file mode 100644 index 00000000..2d53a62a --- /dev/null +++ b/app/src/main/play/listings/pl-PL/title.txt @@ -0,0 +1 @@ +Wulkanowy Dzienniczek 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 15e6ebbf..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/icon/icon.png b/app/src/main/play/pl-PL/listing/icon/icon.png deleted file mode 100644 index 7cad5c2f..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/attendance-dialog.png b/app/src/main/play/pl-PL/listing/phoneScreenshots/attendance-dialog.png deleted file mode 100644 index 3f4ceb7e..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/exams.png b/app/src/main/play/pl-PL/listing/phoneScreenshots/exams.png deleted file mode 100644 index 5a055ff4..00000000 Binary files a/app/src/main/play/pl-PL/listing/phoneScreenshots/exams.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 58c22c51..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/timetable-widget.png b/app/src/main/play/pl-PL/listing/phoneScreenshots/timetable-widget.png deleted file mode 100644 index 832db0b6..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/phoneScreenshots/timetable.png b/app/src/main/play/pl-PL/listing/phoneScreenshots/timetable.png deleted file mode 100644 index e9e66bbb..00000000 Binary files a/app/src/main/play/pl-PL/listing/phoneScreenshots/timetable.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 b5fc7491..00000000 --- a/app/src/main/play/pl-PL/listing/shortdescription +++ /dev/null @@ -1 +0,0 @@ -Nieoficjalna aplikacja 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 e491efb9..00000000 --- a/app/src/main/play/pl-PL/listing/title +++ /dev/null @@ -1 +0,0 @@ -Wulkanowy Dzienniczek UONET+ 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 ff412b22..00000000 --- a/app/src/main/play/pl-PL/whatsnew +++ /dev/null @@ -1,5 +0,0 @@ -Wersja 0.5.1: -- dodano wagę ocen na liście -- naprawiono błędy podczas logowania -- zmieniono nasycenie kolorów ocen -- wyłączenie synchronizacji podczas wakacji diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt new file mode 100644 index 00000000..7fe143d8 --- /dev/null +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -0,0 +1,14 @@ +Wersja 0.9.0 + +Dodaliśmy: +- zarządzanie dostępem mobilnym do dziennika +- wyświetlanie sumy punktów (jeśli dziennik obsługuje) +- wyświetlanie informacji o wygasłej sesji, gdy zostanie zmienione hasło + +Naprawiliśmy: +- wykrywanie uczniów przy logowaniu, jeśli symbol zawiera cyfry +- błędne wyświetlanie średniej +- synchronizację w tle na danych komórkowych przy domyślnych ustawieniach +- rzadkie błędy podczas ładowania szczęśliwego numerka + +Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases diff --git a/app/src/main/res/drawable-anydpi-v24/ic_stat_notify_lucky_number.xml b/app/src/main/res/drawable-anydpi-v24/ic_stat_notify_lucky_number.xml new file mode 100644 index 00000000..00538168 --- /dev/null +++ b/app/src/main/res/drawable-anydpi-v24/ic_stat_notify_lucky_number.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/app/src/main/res/drawable-hdpi/ic_notify_grade.png b/app/src/main/res/drawable-hdpi/ic_stat_notify_grade.png similarity index 100% rename from app/src/main/res/drawable-hdpi/ic_notify_grade.png rename to app/src/main/res/drawable-hdpi/ic_stat_notify_grade.png diff --git a/app/src/main/res/drawable-hdpi/ic_stat_notify_lucky_number.png b/app/src/main/res/drawable-hdpi/ic_stat_notify_lucky_number.png new file mode 100644 index 00000000..b91c4ae6 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_stat_notify_lucky_number.png 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 new file mode 100644 index 00000000..86d63c58 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_stat_notify_message.png 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 new file mode 100644 index 00000000..b49e4ad2 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_stat_notify_note.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_widget_account.png b/app/src/main/res/drawable-hdpi/ic_widget_account.png new file mode 100644 index 00000000..4cb5ac89 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_widget_account.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_notify_grade.png b/app/src/main/res/drawable-mdpi/ic_stat_notify_grade.png similarity index 100% rename from app/src/main/res/drawable-mdpi/ic_notify_grade.png rename to app/src/main/res/drawable-mdpi/ic_stat_notify_grade.png diff --git a/app/src/main/res/drawable-mdpi/ic_stat_notify_lucky_number.png b/app/src/main/res/drawable-mdpi/ic_stat_notify_lucky_number.png new file mode 100644 index 00000000..bfced4eb Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_stat_notify_lucky_number.png 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 new file mode 100644 index 00000000..73a1653a Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_stat_notify_message.png 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 new file mode 100644 index 00000000..3498f71c Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_stat_notify_note.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_widget_account.png b/app/src/main/res/drawable-mdpi/ic_widget_account.png new file mode 100644 index 00000000..237a6b6c Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_widget_account.png differ diff --git a/app/src/main/res/drawable-night/ic_border.xml b/app/src/main/res/drawable-night/ic_all_divider.xml similarity index 100% rename from app/src/main/res/drawable-night/ic_border.xml rename to app/src/main/res/drawable-night/ic_all_divider.xml diff --git a/app/src/main/res/drawable-v23/img_splash_logo.png b/app/src/main/res/drawable-v23/img_splash_logo.png new file mode 100644 index 00000000..61489d81 Binary files /dev/null and b/app/src/main/res/drawable-v23/img_splash_logo.png 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 new file mode 100644 index 00000000..5fa58e90 --- /dev/null +++ b/app/src/main/res/drawable-v23/layer_splash_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/app/src/main/res/drawable-v23/splash_background.xml b/app/src/main/res/drawable-v23/splash_background.xml deleted file mode 100644 index 611bb2a7..00000000 --- a/app/src/main/res/drawable-v23/splash_background.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/drawable-xhdpi/ic_notify_grade.png b/app/src/main/res/drawable-xhdpi/ic_stat_notify_grade.png similarity index 100% rename from app/src/main/res/drawable-xhdpi/ic_notify_grade.png rename to app/src/main/res/drawable-xhdpi/ic_stat_notify_grade.png diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_notify_lucky_number.png b/app/src/main/res/drawable-xhdpi/ic_stat_notify_lucky_number.png new file mode 100644 index 00000000..49e502ac Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_stat_notify_lucky_number.png 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 new file mode 100644 index 00000000..b373ffd4 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_stat_notify_message.png 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 new file mode 100644 index 00000000..5aa30e5f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_stat_notify_note.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_widget_account.png b/app/src/main/res/drawable-xhdpi/ic_widget_account.png new file mode 100644 index 00000000..0ea798a8 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_widget_account.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_notify_grade.png b/app/src/main/res/drawable-xxhdpi/ic_stat_notify_grade.png similarity index 100% rename from app/src/main/res/drawable-xxhdpi/ic_notify_grade.png rename to app/src/main/res/drawable-xxhdpi/ic_stat_notify_grade.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_lucky_number.png b/app/src/main/res/drawable-xxhdpi/ic_stat_notify_lucky_number.png new file mode 100644 index 00000000..9bab1373 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_stat_notify_lucky_number.png 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 new file mode 100644 index 00000000..dc15bbd9 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_stat_notify_message.png 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 new file mode 100644 index 00000000..db3ec517 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_stat_notify_note.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_widget_account.png b/app/src/main/res/drawable-xxhdpi/ic_widget_account.png new file mode 100644 index 00000000..0f8933b1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_widget_account.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 new file mode 100644 index 00000000..3b40ad3f Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_message.png 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 new file mode 100644 index 00000000..06a9299a Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_note.png differ diff --git a/app/src/main/res/drawable/background_rounded_corner.xml b/app/src/main/res/drawable/background_rounded_corner.xml new file mode 100644 index 00000000..116973b5 --- /dev/null +++ b/app/src/main/res/drawable/background_rounded_corner.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_account_circular_border.xml b/app/src/main/res/drawable/ic_account_circular_border.xml new file mode 100644 index 00000000..8ba66975 --- /dev/null +++ b/app/src/main/res/drawable/ic_account_circular_border.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_action_menu_summary.xml b/app/src/main/res/drawable/ic_all_about_24dp.xml similarity index 72% rename from app/src/main/res/drawable/ic_action_menu_summary.xml rename to app/src/main/res/drawable/ic_all_about_24dp.xml index 1b2a93b8..550f312e 100644 --- a/app/src/main/res/drawable/ic_action_menu_summary.xml +++ b/app/src/main/res/drawable/ic_all_about_24dp.xml @@ -4,10 +4,9 @@ android:viewportHeight="24" android:viewportWidth="24"> + android:fillColor="#FFFFFFFF" + android:pathData="M12,2a10,10 0,1 0,0 20,10 10,0 0,0 0,-20zM12,20a8,8 0,1 1,0 -16,8 8,0 0,1 0,16z" /> diff --git a/app/src/main/res/drawable/ic_all_account_24dp.xml b/app/src/main/res/drawable/ic_all_account_24dp.xml new file mode 100644 index 00000000..dea10cb3 --- /dev/null +++ b/app/src/main/res/drawable/ic_all_account_24dp.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_all_add_24dp.xml b/app/src/main/res/drawable/ic_all_add_24dp.xml new file mode 100644 index 00000000..e3979cd7 --- /dev/null +++ b/app/src/main/res/drawable/ic_all_add_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_all_close_circle_24dp.xml b/app/src/main/res/drawable/ic_all_close_circle_24dp.xml new file mode 100644 index 00000000..3809c797 --- /dev/null +++ b/app/src/main/res/drawable/ic_all_close_circle_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_border.xml b/app/src/main/res/drawable/ic_all_divider.xml similarity index 100% rename from app/src/main/res/drawable/ic_border.xml rename to app/src/main/res/drawable/ic_all_divider.xml diff --git a/app/src/main/res/drawable/ic_exclamation_24dp.xml b/app/src/main/res/drawable/ic_all_note_24dp.xml similarity index 96% rename from app/src/main/res/drawable/ic_exclamation_24dp.xml rename to app/src/main/res/drawable/ic_all_note_24dp.xml index 14950458..988df22c 100644 --- a/app/src/main/res/drawable/ic_exclamation_24dp.xml +++ b/app/src/main/res/drawable/ic_all_note_24dp.xml @@ -7,4 +7,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/drawable/ic_alert_24dp.xml b/app/src/main/res/drawable/ic_all_round_note_24dp.xml similarity index 97% rename from app/src/main/res/drawable/ic_alert_24dp.xml rename to app/src/main/res/drawable/ic_all_round_note_24dp.xml index 1c41b556..f4f1f2c5 100644 --- a/app/src/main/res/drawable/ic_alert_24dp.xml +++ b/app/src/main/res/drawable/ic_all_round_note_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_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index c5ecc747..cfec6390 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,17 +1,27 @@ - - + android:width="108dp" + android:height="108dp" + android:viewportWidth="1926.9231" + android:viewportHeight="1926.9231"> + + + + + + + diff --git a/app/src/main/res/drawable/ic_login_outlined_border.xml b/app/src/main/res/drawable/ic_login_outlined_border.xml new file mode 100644 index 00000000..c5d3c686 --- /dev/null +++ b/app/src/main/res/drawable/ic_login_outlined_border.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_menu_grade_semester_24dp.xml b/app/src/main/res/drawable/ic_menu_grade_semester_24dp.xml new file mode 100644 index 00000000..2a03f630 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_grade_semester_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_attendance_24dp.xml b/app/src/main/res/drawable/ic_menu_main_attendance_24dp.xml similarity index 69% rename from app/src/main/res/drawable/ic_menu_attendance_24dp.xml rename to app/src/main/res/drawable/ic_menu_main_attendance_24dp.xml index c0df16d6..eccc338f 100644 --- a/app/src/main/res/drawable/ic_menu_attendance_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_main_attendance_24dp.xml @@ -6,7 +6,6 @@ android:viewportWidth="24"> + 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" /> diff --git a/app/src/main/res/drawable/ic_menu_main_attendance_summary_24dp.xml b/app/src/main/res/drawable/ic_menu_main_attendance_summary_24dp.xml new file mode 100644 index 00000000..13cfc8fa --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_main_attendance_summary_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_exams_24dp.xml b/app/src/main/res/drawable/ic_menu_main_exam_24dp.xml similarity index 100% rename from app/src/main/res/drawable/ic_menu_exams_24dp.xml rename to app/src/main/res/drawable/ic_menu_main_exam_24dp.xml diff --git a/app/src/main/res/drawable/ic_menu_grade_26dp.xml b/app/src/main/res/drawable/ic_menu_main_grade_26dp.xml similarity index 100% rename from app/src/main/res/drawable/ic_menu_grade_26dp.xml rename to app/src/main/res/drawable/ic_menu_main_grade_26dp.xml 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 new file mode 100644 index 00000000..449f0e28 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_main_homework_24dp.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_menu_main_lessons_completed_24dp.xml b/app/src/main/res/drawable/ic_menu_main_lessons_completed_24dp.xml new file mode 100644 index 00000000..c0664c37 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_main_lessons_completed_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_main_mobile_devices_24dp.xml b/app/src/main/res/drawable/ic_menu_main_mobile_devices_24dp.xml new file mode 100644 index 00000000..150ced43 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_main_mobile_devices_24dp.xml @@ -0,0 +1,9 @@ + + + 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 new file mode 100644 index 00000000..26932b2b --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_main_more_24dp.xml @@ -0,0 +1,9 @@ + + + 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 new file mode 100644 index 00000000..6b3172ba --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_main_note_24dp.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/ic_menu_timetable_24dp.xml b/app/src/main/res/drawable/ic_menu_main_timetable_24dp.xml similarity index 53% rename from app/src/main/res/drawable/ic_menu_timetable_24dp.xml rename to app/src/main/res/drawable/ic_menu_main_timetable_24dp.xml index 63880977..1594285f 100644 --- a/app/src/main/res/drawable/ic_menu_timetable_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_main_timetable_24dp.xml @@ -1,11 +1,10 @@ - + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M14 14H7v2h7m5 3H5V8h14m0-5h-1V1h-2v2H8V1H6v2H5a2 2 0 0 0-2 2v14a2 + 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2m-2 7H7v2h10v-2z" /> diff --git a/app/src/main/res/drawable/ic_menu_other_24dp.xml b/app/src/main/res/drawable/ic_menu_other_24dp.xml deleted file mode 100644 index a0774724..00000000 --- a/app/src/main/res/drawable/ic_menu_other_24dp.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_action_menu_semester.xml b/app/src/main/res/drawable/ic_menu_send_message_24dp.xml similarity index 56% rename from app/src/main/res/drawable/ic_action_menu_semester.xml rename to app/src/main/res/drawable/ic_menu_send_message_24dp.xml index b7acb9df..fcc5acfb 100644 --- a/app/src/main/res/drawable/ic_action_menu_semester.xml +++ b/app/src/main/res/drawable/ic_menu_send_message_24dp.xml @@ -2,9 +2,9 @@ android:width="24dp" android:height="24dp" android:tint="#FFFFFF" - android:viewportHeight="24.0" - android:viewportWidth="24.0"> + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z" /> diff --git a/app/src/main/res/drawable/ic_message_delete_24dp.xml b/app/src/main/res/drawable/ic_message_delete_24dp.xml new file mode 100644 index 00000000..4a577483 --- /dev/null +++ b/app/src/main/res/drawable/ic_message_delete_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_message_forward_24dp.xml b/app/src/main/res/drawable/ic_message_forward_24dp.xml new file mode 100644 index 00000000..2b7c32c7 --- /dev/null +++ b/app/src/main/res/drawable/ic_message_forward_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_message_reply_24dp.xml b/app/src/main/res/drawable/ic_message_reply_24dp.xml new file mode 100644 index 00000000..010cb250 --- /dev/null +++ b/app/src/main/res/drawable/ic_message_reply_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_more_lucky_number_24dp.xml b/app/src/main/res/drawable/ic_more_lucky_number_24dp.xml new file mode 100644 index 00000000..f2e6152d --- /dev/null +++ b/app/src/main/res/drawable/ic_more_lucky_number_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_more_messages_24dp.xml b/app/src/main/res/drawable/ic_more_messages_24dp.xml new file mode 100644 index 00000000..65f32169 --- /dev/null +++ b/app/src/main/res/drawable/ic_more_messages_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_more_settings_24dp.xml b/app/src/main/res/drawable/ic_more_settings_24dp.xml new file mode 100644 index 00000000..fc12cdcf --- /dev/null +++ b/app/src/main/res/drawable/ic_more_settings_24dp.xml @@ -0,0 +1,17 @@ + + + diff --git a/app/src/main/res/drawable/ic_send_message_24dp.xml b/app/src/main/res/drawable/ic_send_message_24dp.xml new file mode 100644 index 00000000..0d82394b --- /dev/null +++ b/app/src/main/res/drawable/ic_send_message_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_swap_30dp.xml b/app/src/main/res/drawable/ic_timetable_swap_30dp.xml similarity index 100% rename from app/src/main/res/drawable/ic_swap_30dp.xml rename to app/src/main/res/drawable/ic_timetable_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 new file mode 100644 index 00000000..34345521 Binary files /dev/null and b/app/src/main/res/drawable/ic_widget_chevron_24dp.png differ diff --git a/app/src/main/res/drawable/ic_widget_clover_24dp.png b/app/src/main/res/drawable/ic_widget_clover_24dp.png new file mode 100644 index 00000000..b1a7832a Binary files /dev/null and b/app/src/main/res/drawable/ic_widget_clover_24dp.png differ diff --git a/app/src/main/res/drawable/img_luckynumber_widget_preview.png b/app/src/main/res/drawable/img_luckynumber_widget_preview.png new file mode 100644 index 00000000..539b0a59 Binary files /dev/null and b/app/src/main/res/drawable/img_luckynumber_widget_preview.png differ diff --git a/app/src/main/res/drawable/img_splash_512px.png b/app/src/main/res/drawable/img_splash_512px.png deleted file mode 100644 index 9d8f403c..00000000 Binary files a/app/src/main/res/drawable/img_splash_512px.png and /dev/null differ diff --git a/app/src/main/res/drawable/img_splash_logo.png b/app/src/main/res/drawable/img_splash_logo.png new file mode 100644 index 00000000..fb521bf6 Binary files /dev/null and b/app/src/main/res/drawable/img_splash_logo.png 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 new file mode 100644 index 00000000..55026025 Binary files /dev/null and b/app/src/main/res/drawable/img_timetable_widget_preview.png differ diff --git a/app/src/main/res/drawable-v15/splash_background.xml b/app/src/main/res/drawable/layer_splash_background.xml similarity index 51% rename from app/src/main/res/drawable-v15/splash_background.xml rename to app/src/main/res/drawable/layer_splash_background.xml index c58c374a..64812f15 100644 --- a/app/src/main/res/drawable-v15/splash_background.xml +++ b/app/src/main/res/drawable/layer_splash_background.xml @@ -1,14 +1,12 @@ - - + - + android:gravity="left|right|top|bottom" + android:src="@drawable/img_splash_logo" /> diff --git a/app/src/main/res/drawable/splash_background.xml b/app/src/main/res/drawable/splash_background.xml deleted file mode 100644 index 0b79b672..00000000 --- a/app/src/main/res/drawable/splash_background.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/widget_timetable_preview.png b/app/src/main/res/drawable/widget_timetable_preview.png deleted file mode 100644 index d9f3538c..00000000 Binary files a/app/src/main/res/drawable/widget_timetable_preview.png and /dev/null differ diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 4a6f08b2..1a86fa92 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -1,137 +1,10 @@ - + android:layout_height="match_parent"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -