diff --git a/.bettercodehub.yml b/.bettercodehub.yml index 349f7675a..cce850d71 100644 --- a/.bettercodehub.yml +++ b/.bettercodehub.yml @@ -1,3 +1,5 @@ -component_depth: 8 +exclude: +- /app/src/main/java/io/github/wulkanowy/data/db/dao/entities/.* +component_depth: 1 languages: -- kotlin +- java diff --git a/.circleci/config.yml b/.circleci/config.yml index b07c9638c..387c5edd5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,11 +7,11 @@ references: container_config: &container_config docker: - - image: circleci/android:api-28 + - image: circleci/android:api-27-alpha working_directory: *workspace_root environment: environment: - _JAVA_OPTS: -Xmx3072m + JVM_OPTS: -Xmx3200m attach_workspace: &attach_workspace attach_workspace: @@ -25,8 +25,6 @@ jobs: build: <<: *container_config steps: - - run: | - curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | bash - checkout - restore_cache: <<: *general_cache_key @@ -34,11 +32,20 @@ jobs: name: Setup environment command: ./gradlew dependencies --no-daemon --stacktrace --console=plain -PdisablePreDex || true - run: - name: Initial build - command: ./gradlew build -x test -x lint -x fabricGenerateResourcesRelease -x packageRelease --no-daemon --stacktrace --console=plain -PdisablePreDex + 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: Run FOSSA - command: fossa --no-ansi || true + name: Initial build + command: ./gradlew build assembleDebug -x test -x lint -x fabricGenerateResourcesRelease --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/ - persist_to_workspace: root: *workspace_root paths: @@ -89,13 +96,35 @@ 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: Setup emulator - command: sdkmanager "system-images;android-19;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-19;default;armeabi-v7a" + command: sdkmanager "system-images;android-16;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-16;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 @@ -111,13 +140,15 @@ jobs: # unlock the emulator screen sleep 30 adb shell input keyevent 82 + - run: + name: Clean project + command: ./gradlew clean --no-daemon --stacktrace --console=plain -PdisablePreDex - run: name: Run instrumented tests - command: ./gradlew clean createDebugCoverageReport jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex + command: ./gradlew createDebugCoverageReport --no-daemon --stacktrace --console=plain -PdisablePreDex - run: name: Collect logs from emulator command: adb logcat -d > ./app/build/reports/logcat_emulator.txt - when: always - run: name: Upload code covarage to codecov command: bash <(curl -s https://codecov.io/bash) -F instrumented @@ -142,8 +173,7 @@ jobs: command: ./gradlew jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex - run: name: Run sonarqube runner - command: if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else ./gradlew -x test -x lint sonarqube -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=$CIRCLE_BRANCH --no-daemon --stacktrace --console=plain -PdisablePreDex; fi - command: "[[ -v CIRCLE_PR_NUMBER ]] && ./gradlew -x test -x lint sonarqube -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=$CIRCLE_BRANCH --no-daemon --stacktrace --console=plain -PdisablePreDex || true" + 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 deploy: <<: *container_config @@ -154,12 +184,11 @@ 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 publish --no-daemon --stacktrace --console=plain -PenableCrashlytics -PdisablePreDex + command: ./gradlew publishRelease --no-daemon --stacktrace --console=plain -PdisablePreDex workflows: version: 2 @@ -182,6 +211,12 @@ workflows: only: /.*/ requires: - build + - api-test: + filters: + tags: + only: /.*/ + requires: + - build - instrumented: filters: tags: @@ -196,6 +231,7 @@ workflows: - build - lint - app-test + - api-test - instrumented - deploy: requires: diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 27d57f599..000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,17 +0,0 @@ -## Co powinno się dziać - - -## Co się dzieje - - -## Jak to zrobić kolejny raz: - - 1. - 2. - 3. - -## Informacje o urządzeniu i dzienniku - - - Wersja aplikacji: - - Wersja Androida: - - Adres URL dziennika: diff --git a/.gitignore b/.gitignore index 6bc531b64..8ad04ebf0 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ local.properties .idea/vcs.xml .idea/workspace.xml .idea/caches/ +.idea/codeStyles/ *.iml # OS-specific files @@ -43,8 +44,7 @@ local.properties .Trashes ehthumbs.db Thumbs.db +.idea/codeStyles/ .idea/caches/ -app/key.p12 -app/upload-key.jks -*.log -.idea/assetWizardSettings.xml +./app/key.p12 +./app/upload-key.jks diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0d1bcd5d2..bf80ae049 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: circleci/android:api-28-alpha +image: circleci/android:api-27-alpha before_script: - export GRADLE_USER_HOME=`pwd`/.gradle diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml new file mode 100644 index 000000000..f5bd97e62 --- /dev/null +++ b/.idea/codeStyleSettings.xml @@ -0,0 +1,226 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 43e9b544a..000000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,147 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 0f7bc519d..000000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 7ac24c777..48777522e 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -8,6 +8,7 @@ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 10824e32f..000000000 --- a/.travis.yml +++ /dev/null @@ -1,68 +0,0 @@ -language: android -jdk: oraclejdk8 - -env: - global: - - ANDROID_API_LEVEL=28 - - ANDROID_BUILD_TOOLS_VERSION=28.0.3 - -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - -#branches: -# only: -# - master -# - 0.8.x - -android: - licenses: - - android-sdk-preview-license-.+ - - android-sdk-license-.+ - - google-gdk-license-.+ - components: - - tools - - platform-tools - # The BuildTools version used by your project - - build-tools-$ANDROID_BUILD_TOOLS_VERSION - # The SDK version used to compile your project - - android-$ANDROID_API_LEVEL - # Additional components - - extra-google-google_play_services - - extra-google-m2repository - - extra-android-m2repository - - addon-google_apis-google-$ANDROID_API_LEVEL - # Android emulator - - android-19 - - sys-img-armeabi-v7a-android-19 - -before_script: - # Launch emulator before the execution - - echo no | android create avd --force -n test -t android-19 --abi armeabi-v7a - - emulator -avd test -no-audio -no-window & - - android-wait-for-emulator - - adb shell input keyevent 82 & - - "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash" - -script: - - ./gradlew dependencies --stacktrace --daemon - - fossa --no-ansi || true - - ./gradlew lint -x fabricGenerateResourcesRelease --stacktrace --daemon - - ./gradlew test -x fabricGenerateResourcesRelease --stacktrace --daemon - - ./gradlew createDebugCoverageReport --stacktrace --daemon - - ./gradlew jacocoTestReport --stacktrace --daemon - - if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else - git fetch --unshallow; - ./gradlew sonarqube -x test -x lint -x fabricGenerateResourcesRelease -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} --stacktrace --daemon; - fi - - | - if [ $TRAVIS_TAG ]; then - gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg; - gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg; - gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg; - ./gradlew publish -PenableCrashlytics --stacktrace; - fi - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/README.md b/README.md index 97a4172c4..ee11a9c6d 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,15 @@ # Wulkanowy [![CircleCI](https://img.shields.io/circleci/project/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://circleci.com/gh/wulkanowy/wulkanowy) -[![Travis](https://img.shields.io/travis/com/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://travis-ci.com/wulkanowy/wulkanowy) [![Bitrise](https://img.shields.io/bitrise/daeff1893f3c8128/master.svg?token=Hjm1ACamk86JDeVVJHOeqQ&style=flat-square)](https://www.bitrise.io/app/daeff1893f3c8128) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![BCH compliance](https://bettercodehub.com/edge/badge/wulkanowy/wulkanowy?branch=master)](https://bettercodehub.com/) -[![Sonarcloud](https://sonarcloud.io/api/project_badges/measure?project=io.github.wulkanowy%3Aapp&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=io.github.wulkanowy%3Aapp) -[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy.svg?type=shield)](https://app.fossa.com/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy?ref=badge_shield) +[![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) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) -[Pobierz wersję beta z Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy&utm_source=vcs) +[Pobierz wersję beta](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/f841f20d8f8b1dc8/builds/master/artifacts/0) -[(Więcej wersji DEV)](https://wulkanowy.github.io/dev.html) +[Pobierz wersję DEV](https://bitrise-redirector.herokuapp.com/v0.1/apps/daeff1893f3c8128/builds/master/artifacts/0) 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 new file mode 100644 index 000000000..5e5eeee37 --- /dev/null +++ b/api/build.gradle @@ -0,0 +1,121 @@ +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 new file mode 100644 index 000000000..c6360d04a --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/Client.java @@ -0,0 +1,255 @@ +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 new file mode 100644 index 000000000..dfe4c4b54 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/Cookies.java @@ -0,0 +1,17 @@ +package io.github.wulkanowy.api; + +import java.util.HashMap; +import java.util.Map; + +class Cookies { + + private Map jar = new HashMap<>(); + + Map getItems() { + return jar; + } + + void addItems(Map items) { + jar.putAll(items); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/DateTimeUtils.kt b/api/src/main/java/io/github/wulkanowy/api/DateTimeUtils.kt new file mode 100644 index 000000000..3b620db50 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/DateTimeUtils.kt @@ -0,0 +1,53 @@ +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 new file mode 100644 index 000000000..179fc8ccc --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/NotLoggedInErrorException.java @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..84ec1aed1 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/SnP.java @@ -0,0 +1,35 @@ +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 new file mode 100644 index 000000000..ba527f6a1 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/StudentAndParent.java @@ -0,0 +1,171 @@ +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 new file mode 100644 index 000000000..bf482969c --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/Vulcan.java @@ -0,0 +1,133 @@ +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 new file mode 100644 index 000000000..0bc0c51fc --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/VulcanException.java @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..24ab48e6c --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/VulcanOfflineException.java @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..29b6a6d99 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceStatistics.java @@ -0,0 +1,89 @@ +package io.github.wulkanowy.api.attendance; + +import org.apache.commons.lang3.math.NumberUtils; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; +import io.github.wulkanowy.api.generic.Month; +import io.github.wulkanowy.api.generic.Subject; + +public class AttendanceStatistics { + + private SnP snp; + + private String attendancePageUrl = "Frekwencja.mvc"; + + public AttendanceStatistics(SnP snp) { + this.snp = snp; + } + + public Types getTypesTable() throws IOException, VulcanException { + return getTypesTable(""); + } + + public Types getTypesTable(String tick) throws IOException, VulcanException { + return getTypesTable(tick, -1); + } + + public List getSubjectList() throws IOException, VulcanException { + Element mainContainer = snp.getSnPPageDocument(attendancePageUrl) + .select(".mainContainer #idPrzedmiot").first(); + + List subjectList = new ArrayList<>(); + + for (Element subject : mainContainer.select("option")) { + subjectList.add(new Subject() + .setId(Integer.parseInt(subject.attr("value"))) + .setName(subject.text()) + ); + } + + return subjectList; + } + + public Types getTypesTable(String tick, Integer subjectId) throws IOException, VulcanException { + Element mainContainer = snp.getSnPPageDocument((attendancePageUrl + + "?data={tick}&idPrzedmiot={subject}") + .replace("{tick}", tick) + .replace("{subject}", subjectId.toString()) + ).select(".mainContainer").first(); + + Element table = mainContainer.select("table:nth-of-type(2)").first(); + + Elements headerCells = table.select("thead th"); + List typeList = new ArrayList<>(); + + Elements typesRows = table.select("tbody tr"); + + // fill types with months + for (Element row : typesRows) { + Elements monthsCells = row.select("td"); + + List monthList = new ArrayList<>(); + + // iterate over month in type, first column is empty, last is `total`; (0, n-1) + for (int i = 1; i < monthsCells.size() - 1; i++) { + monthList.add(new Month() + .setValue(NumberUtils.toInt(monthsCells.get(i).text(), 0)) + .setName(headerCells.get(i).text())); + } + + typeList.add(new Type() + .setTotal(NumberUtils.toInt(monthsCells.last().text(), 0)) + .setName(monthsCells.get(0).text()) + .setMonthList(monthList)); + } + + String total = mainContainer.select("h2").text().split(": ")[1]; + + return new Types() + .setTotal(NumberUtils.toDouble(total.replace("%", "").replace(",", "."))) + .setTypeList(typeList); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java new file mode 100644 index 000000000..7caa70d80 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/AttendanceTable.java @@ -0,0 +1,111 @@ +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 new file mode 100644 index 000000000..a9a1fac4e --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/LessonTypes.java @@ -0,0 +1,24 @@ +package io.github.wulkanowy.api.attendance; + +class LessonTypes { + + static final String CLASS_NOT_EXIST = "x-sp-nieobecny-w-oddziale"; + + static final String CLASS_PRESENCE = "x-obecnosc"; + + static final String CLASS_ABSENCE_UNEXCUSED = "x-nieobecnosc-nieuspr"; + + static final String CLASS_ABSENCE_EXCUSED = "x-nieobecnosc-uspr"; + + static final String CLASS_ABSENCE_FOR_SCHOOL_REASONS = "x-nieobecnosc-przycz-szkol"; + + static final String CLASS_UNEXCUSED_LATENESS = "x-sp-nieusprawiedliwione"; + + static final String CLASS_EXCUSED_LATENESS = "x-sp-spr"; + + static final String CLASS_EXEMPTION = "x-sp-zwolnienie"; + + private LessonTypes() { + throw new IllegalStateException("Utility class"); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/Type.java b/api/src/main/java/io/github/wulkanowy/api/attendance/Type.java new file mode 100644 index 000000000..6deb003e1 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/Type.java @@ -0,0 +1,42 @@ +package io.github.wulkanowy.api.attendance; + +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.generic.Month; + +public class Type { + + private String name = ""; + + private int total = 0; + + private List monthList = new ArrayList<>(); + + public String getName() { + return name; + } + + public Type setName(String name) { + this.name = name; + return this; + } + + public int getTotal() { + return total; + } + + public Type setTotal(int total) { + this.total = total; + return this; + } + + public List getMonthList() { + return monthList; + } + + public Type setMonthList(List monthList) { + this.monthList = monthList; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/attendance/Types.java b/api/src/main/java/io/github/wulkanowy/api/attendance/Types.java new file mode 100644 index 000000000..d1b1777de --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/attendance/Types.java @@ -0,0 +1,29 @@ +package io.github.wulkanowy.api.attendance; + +import java.util.ArrayList; +import java.util.List; + +public class Types { + + private double total = 0; + + private List typeList = new ArrayList<>(); + + public double getTotal() { + return total; + } + + public Types setTotal(double total) { + this.total = total; + return this; + } + + public List getTypeList() { + return typeList; + } + + public Types setTypeList(List typeList) { + this.typeList = typeList; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/exams/Exam.java b/api/src/main/java/io/github/wulkanowy/api/exams/Exam.java new file mode 100644 index 000000000..466bfe4f7 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/exams/Exam.java @@ -0,0 +1,59 @@ +package io.github.wulkanowy.api.exams; + +public class Exam { + + private String subjectAndGroup = ""; + + private String type = ""; + + private String description = ""; + + private String teacher = ""; + + private String entryDate = ""; + + public String getSubjectAndGroup() { + return subjectAndGroup; + } + + public Exam setSubjectAndGroup(String subjectAndGroup) { + this.subjectAndGroup = subjectAndGroup; + return this; + } + + public String getType() { + return type; + } + + public Exam setType(String type) { + this.type = type; + return this; + } + + public String getDescription() { + return description; + } + + public Exam setDescription(String description) { + this.description = description; + return this; + } + + public String getTeacher() { + return teacher; + } + + public Exam setTeacher(String teacher) { + this.teacher = teacher; + return this; + } + + public String getEntryDate() { + return entryDate; + } + + public Exam setEntryDate(String entryDate) { + this.entryDate = entryDate; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/exams/ExamDay.java b/api/src/main/java/io/github/wulkanowy/api/exams/ExamDay.java new file mode 100644 index 000000000..8127e631e --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/exams/ExamDay.java @@ -0,0 +1,19 @@ +package io.github.wulkanowy.api.exams; + +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.generic.Day; + +public class ExamDay extends Day { + + private List examList = new ArrayList<>(); + + public List getExamList() { + return examList; + } + + public void addExam(Exam exam) { + this.examList.add(exam); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java b/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java new file mode 100644 index 000000000..391056ac9 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/exams/ExamsWeek.java @@ -0,0 +1,73 @@ +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 new file mode 100644 index 000000000..fbb5ee0a7 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Day.java @@ -0,0 +1,44 @@ +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 new file mode 100644 index 000000000..5c7c8593b --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Diary.java @@ -0,0 +1,38 @@ +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 new file mode 100644 index 000000000..27bf9bf0b --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Lesson.java @@ -0,0 +1,241 @@ +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 new file mode 100644 index 000000000..e38ef3a32 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Month.java @@ -0,0 +1,26 @@ +package io.github.wulkanowy.api.generic; + +public class Month { + + private String name = ""; + + private int value = 0; + + public String getName() { + return name; + } + + public Month setName(String name) { + this.name = name; + return this; + } + + public int getValue() { + return value; + } + + public Month setValue(int value) { + this.value = value; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/ParamItem.java b/api/src/main/java/io/github/wulkanowy/api/generic/ParamItem.java new file mode 100644 index 000000000..e7edfbf48 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/ParamItem.java @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..c14fc5d26 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/School.kt @@ -0,0 +1,7 @@ +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 new file mode 100644 index 000000000..db4a724dd --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Semester.java @@ -0,0 +1,37 @@ +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 new file mode 100644 index 000000000..4ed5dd373 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Student.java @@ -0,0 +1,37 @@ +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 new file mode 100644 index 000000000..fcf27bef3 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Subject.java @@ -0,0 +1,26 @@ +package io.github.wulkanowy.api.generic; + +public class Subject { + + private int id = -1; + + private String name = ""; + + public int getId() { + return id; + } + + public Subject setId(int id) { + this.id = id; + return this; + } + + public String getName() { + return name; + } + + public Subject setName(String name) { + this.name = name; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/generic/Week.java b/api/src/main/java/io/github/wulkanowy/api/generic/Week.java new file mode 100644 index 000000000..1e46f3948 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/generic/Week.java @@ -0,0 +1,33 @@ +package io.github.wulkanowy.api.generic; + +import java.util.ArrayList; +import java.util.List; + +public class Week { + + private List days = new ArrayList<>(); + + private String startDayDate = ""; + + public T getDay(int index) { + return days.get(index); + } + + public List getDays() { + return days; + } + + public Week setDays(List days) { + this.days = days; + return this; + } + + public String getStartDayDate() { + return startDayDate; + } + + public Week setStartDayDate(String startDayDate) { + this.startDayDate = startDayDate; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/grades/Grade.java b/api/src/main/java/io/github/wulkanowy/api/grades/Grade.java new file mode 100644 index 000000000..31e1bbbd9 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/grades/Grade.java @@ -0,0 +1,100 @@ +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 new file mode 100644 index 000000000..cb9fe4657 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/grades/GradesList.java @@ -0,0 +1,75 @@ +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 new file mode 100644 index 000000000..c7917810e --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/grades/Subject.java @@ -0,0 +1,40 @@ +package io.github.wulkanowy.api.grades; + +public class Subject { + + private String name; + + private String predictedRating; + + private String finalRating; + + public String getName() { + return name; + } + + public Subject setName(String name) { + this.name = name; + + return this; + } + + public String getPredictedRating() { + return predictedRating; + } + + public Subject setPredictedRating(String predictedRating) { + this.predictedRating = predictedRating; + + return this; + } + + public String getFinalRating() { + return finalRating; + } + + public Subject setFinalRating(String finalRating) { + this.finalRating = finalRating; + + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/grades/SubjectsList.java b/api/src/main/java/io/github/wulkanowy/api/grades/SubjectsList.java new file mode 100644 index 000000000..37902d0d8 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/grades/SubjectsList.java @@ -0,0 +1,46 @@ +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 new file mode 100644 index 000000000..de3901ff9 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/AccountPermissionException.java @@ -0,0 +1,10 @@ +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 new file mode 100644 index 000000000..13efd4a13 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/BadCredentialsException.java @@ -0,0 +1,10 @@ +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 new file mode 100644 index 000000000..443a9387b --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/Login.java @@ -0,0 +1,146 @@ +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 new file mode 100644 index 000000000..be7439df0 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/LoginErrorException.java @@ -0,0 +1,10 @@ +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 new file mode 100644 index 000000000..2b8a37005 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/login/StartPage.kt @@ -0,0 +1,45 @@ +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 new file mode 100644 index 000000000..14dca67b7 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/messages/BadRequestException.java @@ -0,0 +1,10 @@ +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 new file mode 100644 index 000000000..331ec8196 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/messages/Message.java @@ -0,0 +1,30 @@ +package io.github.wulkanowy.api.messages; + +import com.google.gson.annotations.SerializedName; + +public class Message { + + @SerializedName("Nieprzeczytana") + public boolean unread; + + @SerializedName("Data") + public String date; + + @SerializedName("Tresc") + public String content; + + @SerializedName("Temat") + public String subject; + + @SerializedName("NadawcaNazwa") + public String sender; + + @SerializedName("IdWiadomosci") + public int messageID; + + @SerializedName("IdNadawca") + public int senderID; + + @SerializedName("Id") + public int id; +} diff --git a/api/src/main/java/io/github/wulkanowy/api/messages/Messages.java b/api/src/main/java/io/github/wulkanowy/api/messages/Messages.java new file mode 100644 index 000000000..ec2a3fba4 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/messages/Messages.java @@ -0,0 +1,99 @@ +package io.github.wulkanowy.api.messages; + +import com.google.gson.Gson; +import com.google.gson.JsonParseException; + +import java.io.IOException; +import java.util.List; + +import io.github.wulkanowy.api.Client; +import io.github.wulkanowy.api.NotLoggedInErrorException; +import io.github.wulkanowy.api.VulcanException; + +public class Messages { + + private static final String BASE_URL = "{schema}://uonetplus-uzytkownik.{host}/{symbol}/"; + + private static final String LIST_BASE_URL = BASE_URL + "Wiadomosc.mvc/"; + + private static final String RECEIVED_URL = LIST_BASE_URL + "GetWiadomosciOdebrane"; + + private static final String SENT_URL = LIST_BASE_URL + "GetWiadomosciWyslane"; + + private static final String DELETED_URL = LIST_BASE_URL + "GetWiadomosciUsuniete"; + + private static final String MESSAGE_URL = LIST_BASE_URL + "GetTrescWiadomosci"; + + public static final int RECEIVED_FOLDER = 1; + + public static final int SENT_FOLDER = 2; + + public static final int DELETED_FOLDER = 3; + + private static final String ERROR_TITLE = "Błąd strony"; + + private Client client; + + public Messages(Client client) { + this.client = client; + } + + public List getReceived() throws IOException, VulcanException { + return getMessages(RECEIVED_URL); + } + + public List getSent() throws IOException, VulcanException { + return getMessages(SENT_URL); + } + + public List getDeleted() throws IOException, VulcanException { + return getMessages(DELETED_URL); + } + + private List getMessages(String url) throws IOException, VulcanException { + String res = client.getJsonStringByUrl(url); + + List messages; + + try { + messages = new Gson().fromJson(res, MessagesContainer.class).data; + } catch (JsonParseException e) { + if (res.contains(ERROR_TITLE)) { + throw new BadRequestException(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 new file mode 100644 index 000000000..f0da01e9d --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt @@ -0,0 +1,33 @@ +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 new file mode 100644 index 000000000..d622b4de3 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/mobile/RegisteredDevices.kt @@ -0,0 +1,38 @@ +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 new file mode 100644 index 000000000..346df832e --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/notes/AchievementsList.java @@ -0,0 +1,36 @@ +package io.github.wulkanowy.api.notes; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +public class AchievementsList { + + private static final String NOTES_PAGE_URL = "UwagiOsiagniecia.mvc/Wszystkie"; + + private SnP snp; + + 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 new file mode 100644 index 000000000..b1fa68b93 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/notes/Note.java @@ -0,0 +1,48 @@ +package io.github.wulkanowy.api.notes; + +public class Note { + + private String date; + + private String teacher; + + private String category; + + private String content; + + public String getDate() { + return date; + } + + public Note setDate(String date) { + this.date = date; + return this; + } + + public String getTeacher() { + return teacher; + } + + public Note setTeacher(String teacher) { + this.teacher = teacher; + return this; + } + + public String getCategory() { + return category; + } + + public Note setCategory(String category) { + this.category = category; + return this; + } + + public String getContent() { + return content; + } + + public Note setContent(String content) { + this.content = content; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java b/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java new file mode 100644 index 000000000..54f57edf4 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/notes/NotesList.java @@ -0,0 +1,45 @@ +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 new file mode 100644 index 000000000..f135163a2 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/school/SchoolData.java @@ -0,0 +1,59 @@ +package io.github.wulkanowy.api.school; + +public class SchoolData { + + private String name = ""; + + private String address = ""; + + private String phoneNumber = ""; + + private String headmaster = ""; + + private String[] pedagogue; + + public String getName() { + return name; + } + + public SchoolData setName(String name) { + this.name = name; + return this; + } + + public String getAddress() { + return address; + } + + public SchoolData setAddress(String address) { + this.address = address; + return this; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public SchoolData setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + return this; + } + + public String getHeadmaster() { + return headmaster; + } + + public SchoolData setHeadmaster(String headmaster) { + this.headmaster = headmaster; + return this; + } + + public String[] getPedagogues() { + return pedagogue; + } + + public SchoolData setPedagogue(String[] pedagogue) { + this.pedagogue = pedagogue; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java b/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java new file mode 100644 index 000000000..f0dcd43de --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/school/SchoolInfo.java @@ -0,0 +1,31 @@ +package io.github.wulkanowy.api.school; + +import org.jsoup.nodes.Element; + +import java.io.IOException; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +public class SchoolInfo { + + private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele"; + + private SnP snp; + + public SchoolInfo(SnP snp) { + this.snp = snp; + } + + public SchoolData getSchoolData() throws IOException, VulcanException { + Element e = snp.getSnPPageDocument(SCHOOL_PAGE_URL) + .select(".mainContainer > article").get(0); + + return new SchoolData() + .setName(snp.getRowDataChildValue(e, 1)) + .setAddress(snp.getRowDataChildValue(e, 2)) + .setPhoneNumber(snp.getRowDataChildValue(e, 3)) + .setHeadmaster(snp.getRowDataChildValue(e, 4)) + .setPedagogue(snp.getRowDataChildValue(e, 5).split(", ")); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/school/Subject.java b/api/src/main/java/io/github/wulkanowy/api/school/Subject.java new file mode 100644 index 000000000..0a3c0957d --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/school/Subject.java @@ -0,0 +1,26 @@ +package io.github.wulkanowy.api.school; + +public class Subject { + + private String name = ""; + + private String[] teachers; + + public String getName() { + return name; + } + + public Subject setName(String name) { + this.name = name; + return this; + } + + public String[] getTeachers() { + return teachers; + } + + public Subject setTeachers(String[] teachers) { + this.teachers = teachers; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/school/TeachersData.java b/api/src/main/java/io/github/wulkanowy/api/school/TeachersData.java new file mode 100644 index 000000000..7d41162e5 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/school/TeachersData.java @@ -0,0 +1,39 @@ +package io.github.wulkanowy.api.school; + +import java.util.List; + +public class TeachersData { + + private String className = ""; + + private String[] classTeacher; + + private List subjects; + + public String getClassName() { + return className; + } + + public TeachersData setClassName(String className) { + this.className = className; + return this; + } + + public String[] getClassTeacher() { + return classTeacher; + } + + public TeachersData setClassTeacher(String[] classTeacher) { + this.classTeacher = classTeacher; + return this; + } + + public List getSubjects() { + return subjects; + } + + public TeachersData setSubjects(List subjects) { + this.subjects = subjects; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java b/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java new file mode 100644 index 000000000..ec8429bd9 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/school/TeachersInfo.java @@ -0,0 +1,43 @@ +package io.github.wulkanowy.api.school; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +public class TeachersInfo { + + private static final String SCHOOL_PAGE_URL = "Szkola.mvc/Nauczyciele"; + + private SnP snp; + + public TeachersInfo(SnP snp) { + this.snp = snp; + } + + public TeachersData getTeachersData() throws IOException, VulcanException { + Document doc = snp.getSnPPageDocument(SCHOOL_PAGE_URL); + Elements rows = doc.select(".mainContainer > table tbody tr"); + String description = doc.select(".mainContainer > p").first().text(); + + List subjects = new ArrayList<>(); + + for (Element subject : rows) { + subjects.add(new Subject() + .setName(subject.select("td").get(1).text()) + .setTeachers(subject.select("td").get(2).text().split(", ")) + ); + } + + return new TeachersData() + .setClassName(description.split(", ")[0].split(": ")[1].trim()) + .setClassTeacher(description.split("Wychowawcy:")[1].trim().split(", ")) + .setSubjects(subjects); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/timetable/LessonTypes.java b/api/src/main/java/io/github/wulkanowy/api/timetable/LessonTypes.java new file mode 100644 index 000000000..be1f44e91 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/timetable/LessonTypes.java @@ -0,0 +1,16 @@ +package io.github.wulkanowy.api.timetable; + +class LessonTypes { + + static final String CLASS_PLANNING = "x-treelabel-ppl"; + + static final String CLASS_REALIZED = "x-treelabel-rlz"; + + static final String CLASS_MOVED_OR_CANCELED = "x-treelabel-inv"; + + static final String CLASS_NEW_MOVED_IN_OR_CHANGED = "x-treelabel-zas"; + + private LessonTypes() { + throw new IllegalStateException("Utility class"); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java b/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java new file mode 100644 index 000000000..95c40167c --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/timetable/Timetable.java @@ -0,0 +1,243 @@ +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 new file mode 100644 index 000000000..1aa29de28 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/timetable/TimetableDay.java @@ -0,0 +1,26 @@ +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 new file mode 100644 index 000000000..3d6fc6d54 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/AddressData.java @@ -0,0 +1,40 @@ +package io.github.wulkanowy.api.user; + +public class AddressData { + + private String address = ""; + + private String registeredAddress = ""; + + private String correspondenceAddress = ""; + + public String getAddress() { + return address; + } + + public AddressData setAddress(String address) { + this.address = address; + + return this; + } + + public String getRegisteredAddress() { + return registeredAddress; + } + + public AddressData setRegisteredAddress(String registeredAddress) { + this.registeredAddress = registeredAddress; + + return this; + } + + public String getCorrespondenceAddress() { + return correspondenceAddress; + } + + public AddressData setCorrespondenceAddress(String correspondenceAddress) { + this.correspondenceAddress = correspondenceAddress; + + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/BasicInformation.java b/api/src/main/java/io/github/wulkanowy/api/user/BasicInformation.java new file mode 100644 index 000000000..4edc5117d --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/BasicInformation.java @@ -0,0 +1,70 @@ +package io.github.wulkanowy.api.user; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; + +import java.io.IOException; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +public class BasicInformation { + + private static final String STUDENT_DATA_PAGE_URL = "Uczen.mvc/DanePodstawowe"; + + private static final String CONTENT_QUERY = ".mainContainer > article"; + + private Document studentDataPageDocument; + + private SnP snp; + + public BasicInformation(SnP snp) { + this.snp = snp; + } + + public Document getStudentDataPageDocument() throws IOException, VulcanException { + if (null == studentDataPageDocument) { + studentDataPageDocument = snp.getSnPPageDocument(STUDENT_DATA_PAGE_URL); + } + + return studentDataPageDocument; + } + + public PersonalData getPersonalData() throws IOException, VulcanException { + Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(0); + + String name = snp.getRowDataChildValue(e, 1); + String[] names = name.split(" "); + + return new PersonalData() + .setName(name) + .setFirstName(names[0]) + .setSurname(names[names.length - 1]) + .setFirstAndLastName(names[0] + " " + names[names.length - 1]) + .setDateAndBirthPlace(snp.getRowDataChildValue(e, 2)) + .setPesel(snp.getRowDataChildValue(e, 3)) + .setGender(snp.getRowDataChildValue(e, 4)) + .setPolishCitizenship("Tak".equals(snp.getRowDataChildValue(e, 5))) + .setFamilyName(snp.getRowDataChildValue(e, 6)) + .setParentsNames(snp.getRowDataChildValue(e, 7)); + } + + public AddressData getAddressData() throws IOException, VulcanException { + Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(1); + + return new AddressData() + .setAddress(snp.getRowDataChildValue(e, 1)) + .setRegisteredAddress(snp.getRowDataChildValue(e, 2)) + .setCorrespondenceAddress(snp.getRowDataChildValue(e, 3)); + + } + + public ContactDetails getContactDetails() throws IOException, VulcanException { + Element e = getStudentDataPageDocument().select(CONTENT_QUERY).get(2); + + return new ContactDetails() + .setPhoneNumber(snp.getRowDataChildValue(e, 1)) + .setCellPhoneNumber(snp.getRowDataChildValue(e, 2)) + .setEmail(snp.getRowDataChildValue(e, 3)); + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/ContactDetails.java b/api/src/main/java/io/github/wulkanowy/api/user/ContactDetails.java new file mode 100644 index 000000000..c8556944e --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/ContactDetails.java @@ -0,0 +1,37 @@ +package io.github.wulkanowy.api.user; + +public class ContactDetails { + + private String phoneNumber = ""; + + private String cellPhoneNumber = ""; + + private String email = ""; + + public String getPhoneNumber() { + return phoneNumber; + } + + public ContactDetails setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + return this; + } + + public String getCellPhoneNumber() { + return cellPhoneNumber; + } + + public ContactDetails setCellPhoneNumber(String cellPhoneNumber) { + this.cellPhoneNumber = cellPhoneNumber; + return this; + } + + public String getEmail() { + return email; + } + + public ContactDetails setEmail(String email) { + this.email = email; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/FamilyInformation.java b/api/src/main/java/io/github/wulkanowy/api/user/FamilyInformation.java new file mode 100644 index 000000000..f5a459ee8 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/FamilyInformation.java @@ -0,0 +1,41 @@ +package io.github.wulkanowy.api.user; + +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.api.SnP; +import io.github.wulkanowy.api.VulcanException; + +public class FamilyInformation { + + private static final String STUDENT_DATA_PAGE_URL = "Uczen.mvc/DanePodstawowe"; + + private SnP snp; + + public FamilyInformation(SnP snp) { + this.snp = snp; + } + + public List getFamilyMembers() throws IOException, VulcanException { + Elements membersElements = snp.getSnPPageDocument(STUDENT_DATA_PAGE_URL) + .select(".mainContainer > article:nth-of-type(n+4)"); + + List familyMembers = new ArrayList<>(); + + for (Element e : membersElements) { + familyMembers.add(new FamilyMember() + .setName(snp.getRowDataChildValue(e, 1)) + .setKinship(snp.getRowDataChildValue(e, 2)) + .setAddress(snp.getRowDataChildValue(e, 3)) + .setTelephones(snp.getRowDataChildValue(e, 4)) + .setEmail(snp.getRowDataChildValue(e, 5)) + ); + } + + return familyMembers; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/FamilyMember.java b/api/src/main/java/io/github/wulkanowy/api/user/FamilyMember.java new file mode 100644 index 000000000..dfb966745 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/FamilyMember.java @@ -0,0 +1,59 @@ +package io.github.wulkanowy.api.user; + +public class FamilyMember { + + private String name = ""; + + private String kinship = ""; + + private String address = ""; + + private String telephones = ""; + + private String email = ""; + + public String getName() { + return name; + } + + public FamilyMember setName(String name) { + this.name = name; + return this; + } + + public String getKinship() { + return kinship; + } + + public FamilyMember setKinship(String kinship) { + this.kinship = kinship; + return this; + } + + public String getAddress() { + return address; + } + + public FamilyMember setAddress(String address) { + this.address = address; + return this; + } + + public String getTelephones() { + return telephones; + } + + public FamilyMember setTelephones(String telephones) { + this.telephones = telephones; + return this; + } + + public String getEmail() { + return email; + } + + public FamilyMember setEmail(String email) { + this.email = email; + return this; + } +} diff --git a/api/src/main/java/io/github/wulkanowy/api/user/PersonalData.java b/api/src/main/java/io/github/wulkanowy/api/user/PersonalData.java new file mode 100644 index 000000000..d2991e613 --- /dev/null +++ b/api/src/main/java/io/github/wulkanowy/api/user/PersonalData.java @@ -0,0 +1,114 @@ +package io.github.wulkanowy.api.user; + +public class PersonalData { + + private String name = ""; + + private String firstName = ""; + + private String surname = ""; + + private String firstAndLastName = ""; + + private String dateAndBirthPlace = ""; + + private String pesel = ""; + + private String gender = ""; + + private boolean isPolishCitizenship; + + private String familyName = ""; + + private String parentsNames = ""; + + public String getName() { + return name; + } + + public PersonalData setName(String name) { + this.name = name; + return this; + } + + public String getFirstName() { + return firstName; + } + + public PersonalData setFirstName(String firstName) { + this.firstName = firstName; + return this; + } + + public String getSurname() { + return surname; + } + + public PersonalData setSurname(String surname) { + this.surname = surname; + return this; + } + + public String getFirstAndLastName() { + return firstAndLastName; + } + + public PersonalData setFirstAndLastName(String firstAndLastName) { + this.firstAndLastName = firstAndLastName; + return this; + } + + public String getDateAndBirthPlace() { + return dateAndBirthPlace; + } + + public PersonalData setDateAndBirthPlace(String dateAndBirthPlace) { + this.dateAndBirthPlace = dateAndBirthPlace; + return this; + } + + public String getPesel() { + return pesel; + } + + public PersonalData setPesel(String pesel) { + this.pesel = pesel; + return this; + } + + public String getGender() { + return gender; + } + + public PersonalData setGender(String gender) { + this.gender = gender; + return this; + } + + public boolean isPolishCitizenship() { + return isPolishCitizenship; + } + + public PersonalData setPolishCitizenship(boolean polishCitizenship) { + isPolishCitizenship = polishCitizenship; + return this; + } + + public String getFamilyName() { + return familyName; + } + + public PersonalData setFamilyName(String familyName) { + this.familyName = familyName; + return this; + } + + public String getParentsNames() { + return parentsNames; + } + + public PersonalData setParentsNames(String parentsNames) { + this.parentsNames = parentsNames; + return this; + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/ClientTest.java b/api/src/test/java/io/github/wulkanowy/api/ClientTest.java new file mode 100644 index 000000000..298affac1 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/ClientTest.java @@ -0,0 +1,78 @@ +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 new file mode 100644 index 000000000..1105b2a72 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/DateTimeUtilsTest.kt @@ -0,0 +1,53 @@ +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 new file mode 100644 index 000000000..94dd47b87 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/FixtureHelper.java @@ -0,0 +1,12 @@ +package io.github.wulkanowy.api; + +import java.io.InputStream; +import java.util.Scanner; + +public class FixtureHelper { + + public static String getAsString(InputStream inputStream) { + Scanner s = new Scanner(inputStream).useDelimiter("\\A"); + return s.hasNext() ? s.next() : ""; + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java new file mode 100644 index 000000000..4c6443b1e --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTest.java @@ -0,0 +1,103 @@ +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 new file mode 100644 index 000000000..764e8009e --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/StudentAndParentTestCase.java @@ -0,0 +1,28 @@ +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 new file mode 100644 index 000000000..287935c00 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/VulcanTest.java @@ -0,0 +1,31 @@ +package io.github.wulkanowy.api; + +import org.hamcrest.CoreMatchers; +import org.junit.Assert; +import org.junit.Test; + +public class VulcanTest { + + @Test(expected = NotLoggedInErrorException.class) + public void getClientWithoutLoginTest() throws Exception { + Vulcan vulcan = new Vulcan(); + + vulcan.getClient(); + } + + @Test + public void getClientTest() throws Exception { + Vulcan vulcan = new Vulcan(); + vulcan.setCredentials("email", "password", "symbol", null, 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 new file mode 100644 index 000000000..ca50f3789 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceStatisticsTest.java @@ -0,0 +1,144 @@ +package io.github.wulkanowy.api.attendance; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; +import io.github.wulkanowy.api.generic.Month; + +public class AttendanceStatisticsTest extends StudentAndParentTestCase { + + private AttendanceStatistics excellent; + + private AttendanceStatistics full; + + @Before + public void setUp() throws Exception { + this.excellent = new AttendanceStatistics(getSnp("Frekwencja-excellent.html")); + this.full = new AttendanceStatistics(getSnp("Frekwencja-full.html")); + } + + @Test + public void getSubjectList() throws Exception { + Assert.assertEquals(26, excellent.getSubjectList().size()); + Assert.assertEquals(23, full.getSubjectList().size()); + } + + @Test + public void getSubjectListId() throws Exception { + Assert.assertEquals(-1, excellent.getSubjectList().get(0).getId()); + Assert.assertEquals(63, excellent.getSubjectList().get(10).getId()); + Assert.assertEquals(0, excellent.getSubjectList().get(25).getId()); + + Assert.assertEquals(-1, full.getSubjectList().get(0).getId()); + Assert.assertEquals(108, full.getSubjectList().get(14).getId()); + Assert.assertEquals(492, full.getSubjectList().get(21).getId()); + } + + @Test + public void getSubjectListName() throws Exception { + Assert.assertEquals("Wszystkie", excellent.getSubjectList().get(0).getName()); + Assert.assertEquals("Fizyka", excellent.getSubjectList().get(8).getName()); + Assert.assertEquals("Sieci komputerowe i administrowanie sieciami", + excellent.getSubjectList().get(21).getName()); + + Assert.assertEquals("Praktyka zawodowa", full.getSubjectList().get(11).getName()); + Assert.assertEquals("Użytkowanie urządzeń peryferyjnych komputera", + full.getSubjectList().get(16).getName()); + Assert.assertEquals("Brak opisu lekcji", full.getSubjectList().get(22).getName()); + } + + @Test + public void getTypesTotal() throws Exception { + Assert.assertEquals(100.0, excellent.getTypesTable().getTotal(), 0); + Assert.assertEquals(80.94, full.getTypesTable().getTotal(), 0); + } + + @Test + public void getTypeName() throws Exception { + List typeList1 = excellent.getTypesTable().getTypeList(); + Assert.assertEquals("Obecność", typeList1.get(0).getName()); + Assert.assertEquals("Nieobecność nieusprawiedliwiona", typeList1.get(1).getName()); + Assert.assertEquals("Nieobecność usprawiedliwiona", typeList1.get(2).getName()); + Assert.assertEquals("Nieobecność z przyczyn szkolnych", typeList1.get(3).getName()); + + List typeList2 = full.getTypesTable().getTypeList(); + Assert.assertEquals("Spóźnienie nieusprawiedliwione", typeList2.get(4).getName()); + Assert.assertEquals("Spóźnienie usprawiedliwione", typeList2.get(5).getName()); + Assert.assertEquals("Zwolnienie", typeList2.get(6).getName()); + } + + @Test + public void getTypeTotal() throws Exception { + List typeList1 = excellent.getTypesTable().getTypeList(); + Assert.assertEquals(1211, typeList1.get(0).getTotal()); + Assert.assertEquals(0, typeList1.get(1).getTotal()); + Assert.assertEquals(0, typeList1.get(2).getTotal()); + Assert.assertEquals(0, typeList1.get(3).getTotal()); + Assert.assertEquals(0, typeList1.get(4).getTotal()); + Assert.assertEquals(0, typeList1.get(5).getTotal()); + Assert.assertEquals(0, typeList1.get(6).getTotal()); + + List typeList2 = full.getTypesTable().getTypeList(); + Assert.assertEquals(822, typeList2.get(0).getTotal()); + Assert.assertEquals(6, typeList2.get(1).getTotal()); + Assert.assertEquals(192, typeList2.get(2).getTotal()); + Assert.assertEquals(7, typeList2.get(3).getTotal()); + Assert.assertEquals(12, typeList2.get(4).getTotal()); + Assert.assertEquals(1, typeList2.get(5).getTotal()); + Assert.assertEquals(2, typeList2.get(6).getTotal()); + } + + @Test + public void getTypeList() throws Exception { + List typesList1 = excellent.getTypesTable().getTypeList(); + Assert.assertEquals(12, typesList1.get(0).getMonthList().size()); + Assert.assertEquals(12, typesList1.get(5).getMonthList().size()); + + List typesList2 = full.getTypesTable().getTypeList(); + Assert.assertEquals(12, typesList2.get(0).getMonthList().size()); + Assert.assertEquals(12, typesList2.get(5).getMonthList().size()); + } + + @Test + public void getMonthList() throws Exception { + List typeList1 = excellent.getTypesTable().getTypeList(); + Assert.assertEquals(12, typeList1.get(0).getMonthList().size()); + + List typeList2 = full.getTypesTable().getTypeList(); + Assert.assertEquals(12, typeList2.get(0).getMonthList().size()); + } + + @Test + public void getMonthName() throws Exception { + List monthsList1 = excellent.getTypesTable().getTypeList().get(0).getMonthList(); + Assert.assertEquals("IX", monthsList1.get(0).getName()); + Assert.assertEquals("III", monthsList1.get(6).getName()); + Assert.assertEquals("VIII", monthsList1.get(11).getName()); + + List monthsList2 = full.getTypesTable().getTypeList().get(0).getMonthList(); + Assert.assertEquals("XI", monthsList2.get(2).getName()); + Assert.assertEquals("II", monthsList2.get(5).getName()); + Assert.assertEquals("VI", monthsList2.get(9).getName()); + } + + @Test + public void getMonthValue() throws Exception { + List monthsList1 = excellent.getTypesTable().getTypeList().get(0).getMonthList(); + Assert.assertEquals(142, monthsList1.get(0).getValue()); + Assert.assertEquals(131, monthsList1.get(4).getValue()); + Assert.assertEquals(139, monthsList1.get(7).getValue()); + Assert.assertEquals(114, monthsList1.get(9).getValue()); + Assert.assertEquals(0, monthsList1.get(11).getValue()); + + List typeList1 = full.getTypesTable().getTypeList(); + Assert.assertEquals(135, typeList1.get(0).getMonthList().get(0).getValue()); + Assert.assertEquals(7, typeList1.get(3).getMonthList().get(5).getValue()); + Assert.assertEquals(1, typeList1.get(5).getMonthList().get(0).getValue()); + Assert.assertEquals(27, typeList1.get(2).getMonthList().get(9).getValue()); + Assert.assertEquals(0, typeList1.get(0).getMonthList().get(11).getValue()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java new file mode 100644 index 000000000..40ff4f065 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/attendance/AttendanceTableTest.java @@ -0,0 +1,165 @@ +package io.github.wulkanowy.api.attendance; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class AttendanceTableTest extends StudentAndParentTestCase { + + private AttendanceTable excellent; + + private AttendanceTable full; + + @Before + public void setUp() throws Exception { + excellent = new AttendanceTable(getSnp("Frekwencja-excellent.html")); + full = new AttendanceTable(getSnp("Frekwencja-full.html")); + } + + @Test + public void getWeekStartByDate() throws Exception { + Assert.assertEquals("2015-08-31", excellent.getWeekTable().getStartDayDate()); + Assert.assertEquals("2016-09-05", full.getWeekTable().getStartDayDate()); + } + + @Test + public void getWeekDaysNumber() throws Exception { + Assert.assertEquals(5, excellent.getWeekTable().getDays().size()); + Assert.assertEquals(5, full.getWeekTable().getDays().size()); + } + + @Test + public void getDayLessonsNumber() throws Exception { + Assert.assertEquals(14, excellent.getWeekTable().getDay(0).getLessons().size()); + Assert.assertEquals(14, full.getWeekTable().getDay(0).getLessons().size()); + } + + @Test + public void getDayDate() throws Exception { + Assert.assertEquals("2015-08-31", excellent.getWeekTable().getDay(0).getDate()); + Assert.assertEquals("2015-09-02", excellent.getWeekTable().getDay(2).getDate()); + Assert.assertEquals("2015-09-04", excellent.getWeekTable().getDay(4).getDate()); + + Assert.assertEquals("2016-09-05", full.getWeekTable().getDay(0).getDate()); + Assert.assertEquals("2016-09-07", full.getWeekTable().getDay(2).getDate()); + Assert.assertEquals("2016-09-09", full.getWeekTable().getDay(4).getDate()); + } + + @Test + public void getLessonSubject() throws Exception { + Assert.assertEquals("", + excellent.getWeekTable().getDay(0).getLesson(7).getSubject()); + Assert.assertEquals("Uroczyste rozpoczęcie roku szkolnego 2015/2016", + excellent.getWeekTable().getDay(1).getLesson(1).getSubject()); + Assert.assertEquals("Geografia", + excellent.getWeekTable().getDay(3).getLesson(4).getSubject()); + + Assert.assertEquals("Naprawa komputera", + full.getWeekTable().getDay(1).getLesson(8).getSubject()); + Assert.assertEquals("Religia", + full.getWeekTable().getDay(3).getLesson(1).getSubject()); + Assert.assertEquals("Metodologia programowania", + full.getWeekTable().getDay(4).getLesson(5).getSubject()); + } + + @Test + public void getLessonIsNotExist() throws Exception { + Assert.assertTrue(excellent.getWeekTable().getDay(0).getLesson(5).isNotExist()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(1).isNotExist()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isNotExist()); + + Assert.assertFalse(full.getWeekTable().getDay(1).getLesson(12).isAbsenceUnexcused()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceUnexcused()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(5).isAbsenceUnexcused()); + } + + @Test + public void getLessonIsEmpty() throws Exception { + Assert.assertTrue(excellent.getWeekTable().getDay(0).getLesson(0).isEmpty()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(6).isEmpty()); + Assert.assertTrue(excellent.getWeekTable().getDay(4).getLesson(12).isEmpty()); + + Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(9).isEmpty()); + Assert.assertFalse(full.getWeekTable().getDay(2).getLesson(5).isEmpty()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(2).isEmpty()); + } + + @Test + public void getLessonIsPresence() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(7).isPresence()); + Assert.assertTrue(excellent.getWeekTable().getDay(1).getLesson(1).isPresence()); + Assert.assertTrue(excellent.getWeekTable().getDay(3).getLesson(7).isPresence()); + + Assert.assertTrue(full.getWeekTable().getDay(0).getLesson(1).isPresence()); + Assert.assertTrue(full.getWeekTable().getDay(2).getLesson(6).isPresence()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(7).isPresence()); + } + + @Test + public void getLessonIsAbsenceUnexcused() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(7).isAbsenceUnexcused()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(0).isAbsenceUnexcused()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(4).isAbsenceUnexcused()); + + Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(8).isAbsenceUnexcused()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceUnexcused()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isAbsenceUnexcused()); + } + + @Test + public void getLessonIsAbsenceExcused() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(7).isAbsenceExcused()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(0).isAbsenceExcused()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(4).isAbsenceExcused()); + + Assert.assertFalse(full.getWeekTable().getDay(2).getLesson(5).isAbsenceExcused()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceExcused()); + Assert.assertTrue(full.getWeekTable().getDay(4).getLesson(3).isAbsenceExcused()); + } + + @Test + public void getLessonIsAbsenceForSchoolReasons() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isAbsenceForSchoolReasons()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isAbsenceForSchoolReasons()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isAbsenceForSchoolReasons()); + + Assert.assertTrue(full.getWeekTable().getDay(2).getLesson(5).isAbsenceForSchoolReasons()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isAbsenceForSchoolReasons()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isAbsenceForSchoolReasons()); + } + + @Test + public void getLessonIsUnexcusedLateness() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isUnexcusedLateness()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isUnexcusedLateness()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isUnexcusedLateness()); + + Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(6).isUnexcusedLateness()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isUnexcusedLateness()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isUnexcusedLateness()); + } + + @Test + public void getLessonIsExcusedLateness() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isExcusedLateness()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isExcusedLateness()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isExcusedLateness()); + + Assert.assertTrue(full.getWeekTable().getDay(1).getLesson(7).isExcusedLateness()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isExcusedLateness()); + Assert.assertFalse(full.getWeekTable().getDay(4).getLesson(8).isExcusedLateness()); + } + + @Test + public void getLessonIsExemption() throws Exception { + Assert.assertFalse(excellent.getWeekTable().getDay(0).getLesson(4).isExemption()); + Assert.assertFalse(excellent.getWeekTable().getDay(2).getLesson(8).isExemption()); + Assert.assertFalse(excellent.getWeekTable().getDay(4).getLesson(12).isExemption()); + + Assert.assertFalse(full.getWeekTable().getDay(2).getLesson(5).isExemption()); + Assert.assertFalse(full.getWeekTable().getDay(3).getLesson(1).isExemption()); + Assert.assertTrue(full.getWeekTable().getDay(4).getLesson(8).isExemption()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java b/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java new file mode 100644 index 000000000..0a99c4657 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/exams/ExamsWeekTest.java @@ -0,0 +1,111 @@ +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 new file mode 100644 index 000000000..deab7617c --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/grades/GradesListTest.java @@ -0,0 +1,106 @@ +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 new file mode 100644 index 000000000..d32283781 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/grades/SubjectsListTest.java @@ -0,0 +1,82 @@ +package io.github.wulkanowy.api.grades; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class SubjectsListTest extends StudentAndParentTestCase { + + private SubjectsList std; + + private SubjectsList average; + + @Before + public void setUp() throws Exception { + std = new SubjectsList(getSnp("OcenyWszystkie-subjects.html")); + average = new SubjectsList(getSnp("OcenyWszystkie-subjects-average.html")); + } + + @Test + public void getAllTest() throws Exception { + Assert.assertEquals(5, std.getAll().size()); + Assert.assertEquals(5, average.getAll().size()); + } + + @Test + public void getNameTest() throws Exception { + List stdList = std.getAll(); + + Assert.assertEquals("Zachowanie", stdList.get(0).getName()); + Assert.assertEquals("Praktyka zawodowa", stdList.get(1).getName()); + Assert.assertEquals("Metodologia programowania", stdList.get(2).getName()); + Assert.assertEquals("Podstawy przedsiębiorczości", stdList.get(3).getName()); + Assert.assertEquals("Wychowanie do życia w rodzinie", stdList.get(4).getName()); + + List averageList = average.getAll(); + Assert.assertEquals("Zachowanie", averageList.get(0).getName()); + Assert.assertEquals("Język polski", averageList.get(1).getName()); + Assert.assertEquals("Wychowanie fizyczne", averageList.get(2).getName()); + Assert.assertEquals("Język angielski", averageList.get(3).getName()); + Assert.assertEquals("Wiedza o społeczeństwie", averageList.get(4).getName()); + } + + @Test + public void getPredictedRatingTest() throws Exception { + List stdList = std.getAll(); + + Assert.assertEquals("bardzo dobre", stdList.get(0).getPredictedRating()); + Assert.assertEquals("-", stdList.get(1).getPredictedRating()); + Assert.assertEquals("bardzo dobry", stdList.get(2).getPredictedRating()); + Assert.assertEquals("3/4", stdList.get(3).getPredictedRating()); + Assert.assertEquals("-", stdList.get(4).getPredictedRating()); + + List averageList = average.getAll(); + Assert.assertEquals("bardzo dobre", averageList.get(0).getPredictedRating()); + Assert.assertEquals("-", averageList.get(1).getPredictedRating()); + Assert.assertEquals("bardzo dobry", averageList.get(2).getPredictedRating()); + Assert.assertEquals("4/5", averageList.get(3).getPredictedRating()); + Assert.assertEquals("-", averageList.get(4).getPredictedRating()); + } + + @Test + public void getFinalRatingTest() throws Exception { + List stdList = std.getAll(); + + Assert.assertEquals("bardzo dobre", stdList.get(0).getFinalRating()); + Assert.assertEquals("celujący", stdList.get(1).getFinalRating()); + Assert.assertEquals("celujący", stdList.get(2).getFinalRating()); + Assert.assertEquals("dostateczny", stdList.get(3).getFinalRating()); + Assert.assertEquals("-", stdList.get(4).getFinalRating()); + + List averageList = average.getAll(); + Assert.assertEquals("bardzo dobre", averageList.get(0).getFinalRating()); + Assert.assertEquals("dobry", averageList.get(1).getFinalRating()); + Assert.assertEquals("celujący", averageList.get(2).getFinalRating()); + Assert.assertEquals("bardzo dobry", averageList.get(3).getFinalRating()); + Assert.assertEquals("-", averageList.get(4).getFinalRating()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java b/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java new file mode 100644 index 000000000..86a3ef409 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/login/LoginTest.java @@ -0,0 +1,131 @@ +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 new file mode 100644 index 000000000..271b3bb0a --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/login/StartPageTest.kt @@ -0,0 +1,68 @@ +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 new file mode 100644 index 000000000..14fa4627e --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/messages/MessagesTest.java @@ -0,0 +1,89 @@ +package io.github.wulkanowy.api.messages; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.List; + +import io.github.wulkanowy.api.Client; +import io.github.wulkanowy.api.FixtureHelper; +import io.github.wulkanowy.api.NotLoggedInErrorException; + +public class MessagesTest { + + private Client getFixtureAsString(String fixtureFileName) throws Exception { + Client client = Mockito.mock(Client.class); + Mockito.when(client.getJsonStringByUrl(Mockito.anyString())) + .thenReturn(FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName))); + Mockito.when(client.postJsonStringByUrl(Mockito.anyString(), Mockito.any())) + .thenReturn(FixtureHelper.getAsString(getClass().getResourceAsStream(fixtureFileName))); + return client; + } + + @Test + public void getMessages() throws Exception { + Client client = getFixtureAsString("GetWiadomosciOdebrane.json"); + + Messages messages = new Messages(client); + List messageList = messages.getReceived(); + + Assert.assertEquals(true, messageList.get(1).unread); + Assert.assertEquals("2016-03-15 09:00:00", messageList.get(0).date); + Assert.assertEquals(null, messageList.get(0).content); + Assert.assertEquals("Kowalski Jan", messageList.get(0).sender); + Assert.assertEquals(12347, messageList.get(2).id); + } + + @Test + public void getMessagesEmpty() throws Exception { + Client client = getFixtureAsString("GetWiadomosciUsuniete-empty.json"); + + Messages messages = new Messages(client); + List messageList = messages.getSent(); + + Assert.assertTrue(messageList.isEmpty()); + } + + @Test(expected = NotLoggedInErrorException.class) + public void getMessagesError() throws Exception { + Client client = getFixtureAsString("UndefinedError.txt"); + + Messages messages = new Messages(client); + messages.getDeleted(); + } + + @Test(expected = BadRequestException.class) + public void getMessagesBadRequest() throws Exception { + Client client = getFixtureAsString("PageError.html"); + + Messages messages = new Messages(client); + messages.getDeleted(); + } + + @Test + public void getMessage() throws Exception { + Client client = getFixtureAsString("GetTrescWiadomosci.json"); + + Messages messages = new Messages(client); + Message message = messages.getMessage(123, Messages.RECEIVED_FOLDER); + Assert.assertEquals(12345, message.id); + Assert.assertEquals("Witam, …. \nPozdrawiam Krzysztof Czerkas", message.content); + } + + @Test(expected = NotLoggedInErrorException.class) + public void getMessageError() throws Exception { + Client client = getFixtureAsString("UndefinedError.txt"); + + Messages messages = new Messages(client); + messages.getMessage(321, Messages.SENT_FOLDER); + } + + @Test(expected = BadRequestException.class) + public void getMessageBadRequest() throws Exception { + Client client = getFixtureAsString("PageError.html"); + + Messages messages = new Messages(client); + messages.getMessage(1, Messages.DELETED_FOLDER); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt b/api/src/test/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt new file mode 100644 index 000000000..f67310c88 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/mobile/RegisterDevice.kt @@ -0,0 +1,17 @@ +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 new file mode 100644 index 000000000..3cd8b97cc --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/mobile/RegisteredDevicesListTest.kt @@ -0,0 +1,37 @@ +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 new file mode 100644 index 000000000..0d7774bb4 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/notes/AchievementsListTest.java @@ -0,0 +1,36 @@ +package io.github.wulkanowy.api.notes; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class AchievementsListTest extends StudentAndParentTestCase { + + private AchievementsList filledAchievementsList; + + private AchievementsList emptyAchievementsList; + + @Before + public void setUp() throws Exception { + filledAchievementsList = new AchievementsList(getSnp("UwagiOsiagniecia-filled.html")); + emptyAchievementsList = new AchievementsList(getSnp("UwagiOsiagniecia-empty.html")); + } + + @Test + public void getAllAchievementsTest() throws Exception { + Assert.assertEquals(2, filledAchievementsList.getAllAchievements().size()); + Assert.assertEquals(0, emptyAchievementsList.getAllAchievements().size()); + } + + @Test + public void getAchievements() throws Exception { + List filledList = filledAchievementsList.getAllAchievements(); + + Assert.assertEquals("I miejsce w ogólnopolskim konkursie ortograficznym", filledList.get(0)); + Assert.assertEquals("III miejsce w ogólnopolskim konkursie plastycznym", filledList.get(1)); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java b/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java new file mode 100644 index 000000000..304eb01a8 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/notes/NotesListTest.java @@ -0,0 +1,60 @@ +package io.github.wulkanowy.api.notes; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class NotesListTest extends StudentAndParentTestCase { + + private NotesList filled; + + private NotesList empty; + + @Before + public void setUp() throws Exception { + filled = new NotesList(getSnp("UwagiOsiagniecia-filled.html")); + empty = new NotesList(getSnp("UwagiOsiagniecia-empty.html")); + } + + @Test + public void getAllNotesTest() throws Exception { + Assert.assertEquals(3, filled.getAllNotes().size()); + Assert.assertEquals(0, empty.getAllNotes().size()); + } + + @Test + public void getDateTest() throws Exception { + List filledList = filled.getAllNotes(); + + Assert.assertEquals("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 new file mode 100644 index 000000000..947db9b8f --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/school/SchoolInfoTest.java @@ -0,0 +1,48 @@ +package io.github.wulkanowy.api.school; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class SchoolInfoTest extends StudentAndParentTestCase { + + private SchoolInfo schoolInfo; + + @Before + public void setUp() throws Exception { + schoolInfo = new SchoolInfo(getSnp("Szkola.html")); + } + + @Test + public void getNameTest() throws Exception { + Assert.assertEquals("Zespół Szkół nr 64", schoolInfo.getSchoolData().getName()); + } + + @Test + public void getAddressTest() throws Exception { + Assert.assertEquals("ul. Wiśniowa 128, 01-234 Rogalowo, Nibylandia", + schoolInfo.getSchoolData().getAddress()); + } + + @Test + public void getPhoneNumberTest() throws Exception { + Assert.assertEquals("55 5555555", schoolInfo.getSchoolData().getPhoneNumber()); + } + + @Test + public void getHeadmasterTest() throws Exception { + Assert.assertEquals("Antoni Sobczyk", schoolInfo.getSchoolData().getHeadmaster()); + } + + @Test + public void getPedagoguesTest() throws Exception { + Assert.assertArrayEquals(new String[]{ + "Zofia Czerwińska [ZC]", + "Aleksander Krzemiński [AK]", + "Karolina Kowalska [KK]", + "Bartek Dąbrowski [BD]" + }, schoolInfo.getSchoolData().getPedagogues()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/school/TeachersInfoTest.java b/api/src/test/java/io/github/wulkanowy/api/school/TeachersInfoTest.java new file mode 100644 index 000000000..b0da356b1 --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/school/TeachersInfoTest.java @@ -0,0 +1,56 @@ +package io.github.wulkanowy.api.school; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class TeachersInfoTest extends StudentAndParentTestCase { + + private TeachersInfo teachersInfo; + + @Before + public void setUp() throws Exception { + teachersInfo = new TeachersInfo(getSnp("Szkola.html")); + } + + @Test + public void getClassNameTest() throws Exception { + Assert.assertEquals("1a", teachersInfo.getTeachersData().getClassName()); + } + + @Test + public void getClassTeacherTest() throws Exception { + Assert.assertArrayEquals(new String[]{ + "Karolina Kowalska [AN]", + "Antoni Sobczyk [AS]" + }, teachersInfo.getTeachersData().getClassTeacher()); + } + + @Test + public void getTeachersDataSubjectsNameTest() throws Exception { + List subjects = teachersInfo.getTeachersData().getSubjects(); + + Assert.assertEquals("Biologia", subjects.get(0).getName()); + Assert.assertEquals("Język angielski", subjects.get(6).getName()); + } + + @Test + public void getTeachersDataSubjectsTeachersTest() throws Exception { + List subjects = teachersInfo.getTeachersData().getSubjects(); + + Assert.assertArrayEquals(new String[]{"Karolina Kowalska [AN]"}, + subjects.get(0).getTeachers()); + Assert.assertEquals("Karolina Kowalska [AN]", + subjects.get(0).getTeachers()[0]); + + Assert.assertArrayEquals(new String[]{ + "Karolina Kowalska [AN]", + "Mateusz Kowal [MK]", + "Amelia Mazur [AM]" + }, subjects.get(6).getTeachers()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java b/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java new file mode 100644 index 000000000..7b3cce6ee --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/timetable/TimetableTest.java @@ -0,0 +1,253 @@ +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 new file mode 100644 index 000000000..6b729c95a --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/user/BasicInformationTest.java @@ -0,0 +1,106 @@ +package io.github.wulkanowy.api.user; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class BasicInformationTest extends StudentAndParentTestCase { + + private BasicInformation basicInformation; + + @Before + public void setUp() throws Exception { + basicInformation = new BasicInformation(getSnp("UczenDanePodstawowe.html")); + } + + @Test + public void getPersonalFirstNameTest() throws Exception { + Assert.assertEquals("Maria", basicInformation.getPersonalData().getFirstName()); + } + + @Test + public void getPersonalSurnameTest() throws Exception { + Assert.assertEquals("Kamińska", basicInformation.getPersonalData().getSurname()); + } + + @Test + public void getPersonalFirstAndLastNameTest() throws Exception { + Assert.assertEquals("Maria Kamińska", + basicInformation.getPersonalData().getFirstAndLastName()); + } + + @Test + public void getPersonalNameTest() throws Exception { + Assert.assertEquals("Maria Aneta Kamińska", basicInformation.getPersonalData().getName()); + } + + @Test + public void getPersonalDateAndBirthPlaceTest() throws Exception { + Assert.assertEquals("01.01.1900, Warszawa", + basicInformation.getPersonalData().getDateAndBirthPlace()); + } + + @Test + public void getPersonalPeselTest() throws Exception { + Assert.assertEquals("12345678900", basicInformation.getPersonalData().getPesel()); + } + + @Test + public void getPersonalGenderTest() throws Exception { + Assert.assertEquals("Kobieta", basicInformation.getPersonalData().getGender()); + } + + @Test + public void isPersonalPolishCitizenshipTest() throws Exception { + Assert.assertTrue(basicInformation.getPersonalData().isPolishCitizenship()); + } + + @Test + public void getPersonalFamilyNameTest() throws Exception { + Assert.assertEquals("Nowak", basicInformation.getPersonalData().getFamilyName()); + } + + @Test + public void getPersonalParentsNames() throws Exception { + Assert.assertEquals("Gabriela, Kamil", + basicInformation.getPersonalData().getParentsNames()); + } + + @Test + public void getBasicAddressTest() throws Exception { + Assert.assertEquals("ul. Sportowa 16, 00-123 Warszawa", + basicInformation.getAddressData().getAddress()); + } + + @Test + public void getBasicRegisteredAddressTest() throws Exception { + Assert.assertEquals("ul. Sportowa 17, 00-123 Warszawa", + basicInformation.getAddressData().getRegisteredAddress()); + } + + @Test + public void getBasicCorrespondenceAddressTest() throws Exception { + Assert.assertEquals("ul. Sportowa 18, 00-123 Warszawa", + basicInformation.getAddressData().getCorrespondenceAddress()); + } + + @Test + public void getContactPhoneNumberTest() throws Exception { + Assert.assertEquals("005554433", + basicInformation.getContactDetails().getPhoneNumber()); + } + + @Test + public void getContactCellPhoneNumberTest() throws Exception { + Assert.assertEquals("555444333", + basicInformation.getContactDetails().getCellPhoneNumber()); + } + + @Test + public void getContactEmailTest() throws Exception { + Assert.assertEquals("wulkanowy@example.null", + basicInformation.getContactDetails().getEmail()); + } +} diff --git a/api/src/test/java/io/github/wulkanowy/api/user/FamilyInformationTest.java b/api/src/test/java/io/github/wulkanowy/api/user/FamilyInformationTest.java new file mode 100644 index 000000000..b4f858ece --- /dev/null +++ b/api/src/test/java/io/github/wulkanowy/api/user/FamilyInformationTest.java @@ -0,0 +1,59 @@ +package io.github.wulkanowy.api.user; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import io.github.wulkanowy.api.StudentAndParentTestCase; + +public class FamilyInformationTest extends StudentAndParentTestCase { + + private FamilyInformation familyInformation; + + @Before + public void setUp() throws Exception { + familyInformation = new FamilyInformation(getSnp("UczenDanePodstawowe.html")); + } + + @Test + public void getFamilyMembers() throws Exception { + Assert.assertEquals(2, familyInformation.getFamilyMembers().size()); + } + + @Test + public void getNameTest() throws Exception { + List list = familyInformation.getFamilyMembers(); + Assert.assertEquals("Marianna Pająk", list.get(0).getName()); + Assert.assertEquals("Dawid Świątek", list.get(1).getName()); + } + + @Test + public void getKinshipTest() throws Exception { + List list = familyInformation.getFamilyMembers(); + Assert.assertEquals("matka", list.get(0).getKinship()); + Assert.assertEquals("ojciec", list.get(1).getKinship()); + } + + @Test + public void getAddressTest() throws Exception { + List list = familyInformation.getFamilyMembers(); + Assert.assertEquals("ul. Sportowa 16, 00-123 Warszawa", list.get(0).getAddress()); + Assert.assertEquals("ul. Sportowa 18, 00-123 Warszawa", list.get(1).getAddress()); + } + + @Test + public void getTelephonesTest() throws Exception { + List list = familyInformation.getFamilyMembers(); + Assert.assertEquals("555111222", list.get(0).getTelephones()); + Assert.assertEquals("555222111", list.get(1).getTelephones()); + } + + @Test + public void getEmailTest() throws Exception { + List list = familyInformation.getFamilyMembers(); + Assert.assertEquals("wulkanowy@example.null", list.get(0).getEmail()); + Assert.assertEquals("wulkanowy@example.null", list.get(1).getEmail()); + } +} diff --git a/api/src/test/resources/io/github/wulkanowy/api/OcenyWszystkie-semester.html b/api/src/test/resources/io/github/wulkanowy/api/OcenyWszystkie-semester.html new file mode 100644 index 000000000..dc7c6c3b6 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/OcenyWszystkie-semester.html @@ -0,0 +1,22 @@ + + + + + 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 new file mode 100644 index 000000000..d53236777 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/Start-multi.html @@ -0,0 +1,29 @@ + + + + + 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 new file mode 100644 index 000000000..b65625cf1 --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/Start-std.html @@ -0,0 +1,26 @@ + + + + + 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 new file mode 100644 index 000000000..66e2942fd --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/WitrynaUczniaIRodzica.html @@ -0,0 +1,37 @@ + + + + + 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 new file mode 100644 index 000000000..5eaf50dfe --- /dev/null +++ b/api/src/test/resources/io/github/wulkanowy/api/attendance/Frekwencja-excellent.html @@ -0,0 +1,408 @@ + + + + + Witryna ucznia i rodzica – Frekwencja + + + +
+

Frekwencja

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

Statystyki

+
+ + +
+

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

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

Frekwencja

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

Statystyki

+
+ + +
+

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

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

Sprawdziany

+

Tydzień 23.10.2017 - 29.10.2017

+
+

poniedziałek, 23.10.2017

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

wtorek, 24.10.2017

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

piątek, 27.10.2017

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

Oceny

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

Oceny

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

Przerwa techniczna

+

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

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

Uwagi

+

Brak informacji do wyświetlenia

+
+
+

Osiągnięcia

+

Brak informacji do wyświetlenia

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

Uwagi

+

06.06.2017

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

01.12.2016

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

01.10.2016

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

Osiągnięcia

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

Szkoła

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

Nauczyciele

+

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

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

Plan lekcji

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

Plan lekcji

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

Dane podstawowe

+

Dane osobowe

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

Dane adresowe

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

Kontakt

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

Rodzina

+
+
+ Nazwisko i imię: + Marianna Pająk +
+
+ Stopień pokrewieństwa: + matka +
+
+ Adres: + ul. Sportowa 16, 00-123 Warszawa +
+
+ Telefony: + 555111222 +
+
+ E-mail: + wulkanowy@example.null +
+
+
+
+ Nazwisko i imię: + Dawid Świątek +
+
+ Stopień pokrewieństwa: + ojciec +
+
+ Adres: + ul. Sportowa 18, 00-123 Warszawa +
+
+ Telefony: + 555222111 +
+
+ E-mail: + wulkanowy@example.null +
+
+
+
wersja: 17.02.0000.23328
+ + diff --git a/app/sonarqube.gradle b/app/android-sonarqube.gradle similarity index 73% rename from app/sonarqube.gradle rename to app/android-sonarqube.gradle index 52fd2dd8b..b9284eeac 100644 --- a/app/sonarqube.gradle +++ b/app/android-sonarqube.gradle @@ -4,13 +4,16 @@ sonarqube { //noinspection GroovyAssignabilityCheck properties { - def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-28/android.jar" + def files = fileTree("${rootProject.projectDir}/api/build/libs/").filter { it.isFile() }.files.name + def libraries = project.android.sdkDirectory.getPath() + "/platforms/android-27/android.jar," + + "${project.rootDir}/api/build/libs/" + files[0] - property "sonar.projectName", "io.github.wulkanowy:app" - property "sonar.projectKey", "io.github.wulkanowy:app" + property "sonar.projectName", GROUP_ID + ":app" + property "sonar.projectKey", GROUP_ID + ":app" property "sonar.sources", "src/main/java" - property "sonar.exclusions", "build/**,**/*.png,*.iml, **/*generated*," + property "sonar.exclusions", "build/**,**/*.png,*.iml, **/*generated*," + + "src/**/entities/*.java, src/androidTest/**/entities/*.java" property "sonar.import_unknown_files", true // Defines where the java files are diff --git a/app/build.gradle b/app/build.gradle index 3043bc651..779343318 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,39 +1,56 @@ +apply plugin: 'org.greenrobot.greendao' apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-kapt' // sync warning probably caused by bug https://issuetracker.google.com/issues/74537216 apply plugin: 'io.fabric' -apply plugin: 'com.github.triplet.play' apply from: 'jacoco.gradle' -apply from: 'sonarqube.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' } +} android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' + compileSdkVersion 27 + buildToolsVersion '27.0.3' + + playAccountConfigs { + defaultAccountConfig { + serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") + pk12File = file('key.p12') + } + } defaultConfig { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 - targetSdkVersion 28 - versionCode 36 - versionName "0.8.3" - multiDexEnabled true - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + targetSdkVersion 27 + versionCode 16 + versionName "0.5.2" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true + playAccountConfig = playAccountConfigs.defaultAccountConfig manifestPlaceholders = [ - fabric_api_key : System.getenv("FABRIC_API_KEY") ?: "null", - crashlytics_enabled: project.hasProperty("enableCrashlytics") + fabricApiKey: System.getenv("FABRIC_API_KEY") ?: "null" ] - javaCompileOptions { - annotationProcessorOptions { - arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] - } - } - } - - sourceSets { - androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } signingConfigs { @@ -47,108 +64,81 @@ android { buildTypes { release { - buildConfigField "boolean", "CRASHLYTICS_ENABLED", "true" - minifyEnabled true - shrinkResources true + minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.release } debug { - buildConfigField "boolean", "CRASHLYTICS_ENABLED", project.hasProperty("enableCrashlytics") ? "true" : "false" applicationIdSuffix ".dev" versionNameSuffix "-dev" testCoverageEnabled = true - ext.enableCrashlytics = project.hasProperty("enableCrashlytics") - multiDexKeepProguard file('proguard-multidex-rules.pro') + ext.enableCrashlytics = false } } - lintOptions { - disable 'HardwareIds' + testOptions { + unitTests.all { + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen { false } + showStandardStreams = true + } + } } - - 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 'io.github.wulkanowy:api:0.8.3' - + implementation project(':api') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - 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.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.cardview:cardview:1.0.0" - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation "com.google.android.material:material:1.1.0-alpha05" - implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f' - implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + implementation("com.crashlytics.sdk.android:crashlytics:$crashlyticsSdk@aar") { + transitive = true + } + implementation("com.crashlytics.sdk.android:answers:$crashlyticsAnswers@aar") { + transitive = true + } - implementation "androidx.work:work-runtime:2.0.1" - implementation "androidx.work:work-rxjava2:2.0.1" + kapt "com.google.dagger:dagger-compiler:$dagger2" + kapt "com.google.dagger:dagger-android-processor:$dagger2" + kapt "com.jakewharton:butterknife-compiler:$butterknife" - implementation "androidx.room:room-runtime:2.1.0-alpha07" - implementation "androidx.room:room-rxjava2:2.1.0-alpha07" - kapt "androidx.room:room-compiler:2.1.0-alpha07" + debugImplementation "com.amitshekhar.android:debug-db:$debugDb" - implementation "com.google.dagger:dagger-android-support:2.22.1" - kapt "com.google.dagger:dagger-compiler:2.22.1" - kapt "com.google.dagger:dagger-android-processor:2.22.1" - implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0' - kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0' + testImplementation "junit:junit:$junit" + testImplementation "org.mockito:mockito-core:$mockito" - 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.2' - implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' - implementation "io.reactivex.rxjava2:rxjava:2.2.8" - - 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.1" - - implementation "com.mikepenz:aboutlibraries:6.2.3" - implementation 'com.takisoft.preferencex:preferencex:1.0.0' - - implementation 'com.google.firebase:firebase-core:16.0.8' - implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9' - - releaseImplementation 'fr.o80.chucker:library-no-op:2.0.4' - - debugImplementation 'fr.o80.chucker:library:2.0.4' - debugImplementation "com.amitshekhar.android:debug-db:1.0.6" - - testImplementation "junit:junit:4.12" - testImplementation "io.mockk:mockk:1.9.2" - testImplementation "org.mockito:mockito-inline:2.27.0" - testImplementation 'org.threeten:threetenbp:1.3.8' - - androidTestImplementation 'androidx.test:core:1.1.0' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.ext:junit:1.1.0' - androidTestImplementation "io.mockk:mockk-android:1.9.2" - androidTestImplementation 'org.mockito:mockito-android:2.27.0' - androidTestImplementation "androidx.room:room-testing:2.1.0-alpha07" - androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + androidTestImplementation "com.android.support.test:runner:$testRunner" + androidTestImplementation "org.mockito:mockito-android:$mockito" } - -apply plugin: 'com.google.gms.google-services' diff --git a/app/jacoco.gradle b/app/jacoco.gradle index f22c33598..f59b3857e 100644 --- a/app/jacoco.gradle +++ b/app/jacoco.gradle @@ -1,7 +1,7 @@ apply plugin: "jacoco" jacoco { - toolVersion "0.8.3" + toolVersion "0.8.1" reportsDir = file("$buildDir/reports") } @@ -9,6 +9,7 @@ tasks.withType(Test) { jacoco.includeNoLocationClasses = true } +// run ./gradlew clean createDebugCoverageReport jacocoTestReport task jacocoTestReport(type: JacocoReport) { group = "Reporting" @@ -19,29 +20,37 @@ task jacocoTestReport(type: JacocoReport) { html.enabled = true } - def excludes = ['**/R.class', - '**/R$*.class', - '**/BuildConfig.*', - '**/Manifest*.*', - '**/*Test*.*', - 'android/**/*.*', - '**/*Module.*', - '**/*Dagger*.*', - '**/*MembersInjector*.*', - '**/*_Provide*Factory*.*', - '**/*_Factory.*'] + def excludes = [ + "**/R.class", + "**/R\$*.class", + "**/*\$ViewInjector*.*", + "**/BuildConfig.*", + "**/Manifest*.*", + "**/*Test*.*", + "android/**/*.*", + "**/*Fragment.*", + "**/*Activity.*" + ] - classDirectories.setFrom(fileTree( + // generated classes + classDirectories = fileTree( + // Java generated classes on Android project (debug build) dir: "$buildDir/intermediates/classes/debug", excludes: excludes ) + fileTree( + // Kotlin generated classes on Android project (debug build) dir: "$buildDir/tmp/kotlin-classes/debug", excludes: excludes - )) + ) - sourceDirectories.setFrom(files("$project.projectDir/src/main/java")) - executionData.setFrom(fileTree( + // sources + sourceDirectories = files([ + android.sourceSets.main.java.srcDirs, + "src/main/kotlin" + ]) + + executionData = fileTree( dir: project.projectDir, - includes: ["**/*.exec", "**/*.ec"] - )) + includes: ["**/*.exec" , "**/*.ec"] + ) } diff --git a/app/key.p12.gpg b/app/key.p12.gpg deleted file mode 100644 index e9b6d06eb..000000000 Binary files a/app/key.p12.gpg and /dev/null differ diff --git a/app/proguard-multidex-rules.pro b/app/proguard-multidex-rules.pro deleted file mode 100644 index 9ee1737f4..000000000 --- a/app/proguard-multidex-rules.pro +++ /dev/null @@ -1,3 +0,0 @@ --keep class android.support.test.internal** { *; } --keep class org.junit.** { *; } --keep public class io.github.wulkanowy** { *; } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 15b628384..34ed4c38c 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,39 +1,25 @@ -# Optimizations --optimizationpasses 5 --optimizations !code/simplification/arithmetic,!field/*,!class/merging/* --dontusemixedcaseclassnames --dontskipnonpubliclibraryclasses --dontskipnonpubliclibraryclassmembers --dontpreverify --dontobfuscate --allowaccessmodification --repackageclasses '' --verbose +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Users\RicomenPL\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html +# Add any project specific keep options here: -#Config for anallitycs --keepattributes *Annotation* --keepattributes SourceFile,LineNumberTable --keep class com.crashlytics.** {*;} --keep public class * extends java.lang.Exception --dontwarn com.crashlytics.** +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable -#Config for OkHttp --keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase --dontwarn org.codehaus.mojo.animal_sniffer.* --dontwarn okhttp3.internal.platform.ConscryptPlatform --dontwarn javax.annotation.** - - -#Config for ReactiveNetwork --dontwarn com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork --dontwarn io.reactivex.functions.Function --dontwarn rx.internal.util.** --dontwarn sun.misc.Unsafe - -#Config for MPAndroidChart --keep class com.github.mikephil.charting.** { *; } - -#Config for API --keep class io.github.wulkanowy.api.** {*;} +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json deleted file mode 100644 index e42a4a719..000000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json +++ /dev/null @@ -1,1325 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 11, - "identityHash": "478af7daed6ac4563e71826fb70cc8c8", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "endpoint", - "columnName": "endpoint", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "loginType", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id" - ], - "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "semester_id" - ], - "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predictedGrade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "finalGrade", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "grade", - "columnName": "grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "amount", - "columnName": "amount", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semester", - "columnName": "is_semester", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"478af7daed6ac4563e71826fb70cc8c8\")" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json deleted file mode 100644 index 32f943554..000000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json +++ /dev/null @@ -1,1332 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 12, - "identityHash": "972ad26e6488d9a8239f6bd8597af61d", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "endpoint", - "columnName": "endpoint", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "loginType", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "semester_id" - ], - "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predictedGrade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "finalGrade", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "grade", - "columnName": "grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "amount", - "columnName": "amount", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semester", - "columnName": "is_semester", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"972ad26e6488d9a8239f6bd8597af61d\")" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json deleted file mode 100644 index ab554665e..000000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json +++ /dev/null @@ -1,1356 +0,0 @@ -{ - "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/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AccountTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AccountTest.java new file mode 100644 index 000000000..e5330b621 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AccountTest.java @@ -0,0 +1,18 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +public class AccountTest extends AbstractDaoTestLongPk { + + public AccountTest() { + super(AccountDao.class); + } + + @Override + protected Account createEntity(Long key) { + Account entity = new Account(); + entity.setId(key); + return entity; + } + +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java new file mode 100644 index 000000000..0aca371a3 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLessonTest.java @@ -0,0 +1,25 @@ +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 new file mode 100644 index 000000000..cdab015a5 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DayTest.java @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +public class DayTest extends AbstractDaoTestLongPk { + + public DayTest() { + super(DayDao.class); + } + + @Override + protected Day createEntity(Long key) { + Day entity = new Day(); + entity.setId(key); + entity.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 new file mode 100644 index 000000000..2951aec83 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/DiaryTest.java @@ -0,0 +1,19 @@ +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 new file mode 100644 index 000000000..271e811f6 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/ExamTest.java @@ -0,0 +1,18 @@ +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 new file mode 100644 index 000000000..ea0265591 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/GradeTest.java @@ -0,0 +1,20 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +public class GradeTest extends AbstractDaoTestLongPk { + + public GradeTest() { + super(GradeDao.class); + } + + @Override + protected Grade createEntity(Long key) { + Grade entity = new Grade(); + entity.setId(key); + entity.setIsNew(false); + entity.setRead(false); + return entity; + } + +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SchoolTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SchoolTest.java new file mode 100644 index 000000000..13f63e0d0 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SchoolTest.java @@ -0,0 +1,22 @@ +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 new file mode 100644 index 000000000..a18a76742 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SemesterTest.java @@ -0,0 +1,21 @@ +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 new file mode 100644 index 000000000..1ba8fd467 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/StudentTest.java @@ -0,0 +1,21 @@ +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 new file mode 100644 index 000000000..81a2e724f --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SubjectTest.java @@ -0,0 +1,18 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +public class SubjectTest extends AbstractDaoTestLongPk { + + public SubjectTest() { + super(SubjectDao.class); + } + + @Override + protected Subject createEntity(Long key) { + Subject entity = new Subject(); + entity.setId(key); + return entity; + } + +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SymbolTest.java b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SymbolTest.java new file mode 100644 index 000000000..e1bad201b --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/SymbolTest.java @@ -0,0 +1,21 @@ +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 new file mode 100644 index 000000000..190f09b79 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/TimetableLessonTest.java @@ -0,0 +1,24 @@ +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 new file mode 100644 index 000000000..86e7a8be5 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/dao/entities/WeekTest.java @@ -0,0 +1,18 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.test.AbstractDaoTestLongPk; + +public class WeekTest extends AbstractDaoTestLongPk { + + public WeekTest() { + super(WeekDao.class); + } + + @Override + protected Week createEntity(Long key) { + Week entity = new Week(); + entity.setId(key); + return entity; + } + +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt deleted file mode 100644 index 507105f6d..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt +++ /dev/null @@ -1,31 +0,0 @@ -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()) - .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 deleted file mode 100644 index 0bbcc4271..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt +++ /dev/null @@ -1,133 +0,0 @@ -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 deleted file mode 100644 index eb9d02a59..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt +++ /dev/null @@ -1,171 +0,0 @@ -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 deleted file mode 100644 index 7dc93c4a4..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestInternetObservingStrategy.kt +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.wulkanowy.data.repositories - -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingStrategy -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler -import io.reactivex.Observable -import io.reactivex.Single - -class TestInternetObservingStrategy : InternetObservingStrategy { - - override fun checkInternetConnectivity(host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Single { - return Single.just(true) - } - - override fun observeInternetConnectivity(initialIntervalInMs: Int, intervalInMs: Int, host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Observable { - return Observable.just(true) - } - - override fun getDefaultPingHost() = "localhost" -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt deleted file mode 100644 index 69502e742..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendance - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import 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 deleted file mode 100644 index 356073e8e..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.wulkanowy.data.repositories.completedlessons - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class CompletedLessonsLocalTest { - - private lateinit var completedLessonsLocal: CompletedLessonsLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - completedLessonsLocal = CompletedLessonsLocal(testDb.completedLessonsDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - completedLessonsLocal.saveCompletedLessons(listOf( - getCompletedLesson(LocalDate.of(2018, 9, 10), 1), - getCompletedLesson(LocalDate.of(2018, 9, 14), 2), - getCompletedLesson(LocalDate.of(2018, 9, 17), 3) - )) - - val completed = completedLessonsLocal - .getCompletedLessons(Semester(1, 2, "", 1, 3, 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 deleted file mode 100644 index fb76306d9..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.data.repositories.exam - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class ExamLocalTest { - - private lateinit var examLocal: ExamLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build() - examLocal = ExamLocal(testDb.examsDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - examLocal.saveExams(listOf( - Exam(1, 2, LocalDate.of(2018, 9, 10), LocalDate.now(), "", "", "", "", "", ""), - Exam(1, 2, LocalDate.of(2018, 9, 14), LocalDate.now(), "", "", "", "", "", ""), - Exam(1, 2, LocalDate.of(2018, 9, 17), LocalDate.now(), "", "", "", "", "", "") - )) - - val exams = examLocal - .getExams(Semester(1, 2, "", 1, 3, 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 deleted file mode 100644 index 954d0eea2..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import 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 deleted file mode 100644 index a0acb5a76..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt +++ /dev/null @@ -1,182 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import android.os.Build.VERSION_CODES.P -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider.getApplicationContext -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SdkSuppress -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.impl.annotations.SpyK -import io.reactivex.Single -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate.of -import org.threeten.bp.LocalDateTime -import kotlin.test.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 deleted file mode 100644 index e0fd05a82..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import io.github.wulkanowy.api.toDate -import org.threeten.bp.LocalDate -import io.github.wulkanowy.api.grades.Grade as GradeRemote -import io.github.wulkanowy.data.db.entities.Grade as GradeLocal - -fun createGradeLocal(value: Int, weight: Double, date: LocalDate, desc: String, semesterId: Int = 1): GradeLocal { - return GradeLocal( - semesterId = semesterId, - studentId = 1, - modifier = .0, - teacher = "", - subject = "", - date = date, - color = "", - comment = "", - description = desc, - entry = "", - gradeSymbol = "", - value = value, - weight = "", - weightValue = weight - ) -} - -fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String): GradeRemote { - return GradeRemote().apply { - this.value = value - this.weightValue = weight - this.date = date.toDate() - this.description = desc - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt deleted file mode 100644 index 0057a26e5..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt +++ /dev/null @@ -1,69 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradestatistics - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import 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 deleted file mode 100644 index 77ddafb9c..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.wulkanowy.data.repositories.luckynumber - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class LuckyNumberLocalTest { - - private lateinit var luckyNumberLocal: LuckyNumberLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - luckyNumberLocal = LuckyNumberLocal(testDb.luckyNumberDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - luckyNumberLocal.saveLuckyNumber(LuckyNumber(1, LocalDate.of(2019, 1, 20), 14)) - - val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, 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 deleted file mode 100644 index 6edaccdb4..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.data.repositories.recipient - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDateTime -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class RecipientLocalTest { - - private lateinit var recipientLocal: RecipientLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - recipientLocal = RecipientLocal(testDb.recipientDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - recipientLocal.saveRecipients(listOf( - Recipient(1, "2rPracownik", "Kowalski Jan", "Kowalski Jan [KJ] - Pracownik (Fake123456)", 3, 4, 2, "hash"), - Recipient(1, "3rPracownik", "Kowalska Karolina", "Kowalska Karolina [KK] - Pracownik (Fake123456)", 4, 4, 2, "hash"), - Recipient(1, "4rPracownik", "Krupa Stanisław", "Krupa Stanisław [KS] - Uczeń (Fake123456)", 5, 4, 1, "hash") - )) - - val recipients = recipientLocal.getRecipients( - Student("fakelog.cf", "AUTO", "", "", "", 1, "", "", "", "", 1, true, LocalDateTime.now()), - 2, - ReportingUnit(1, 4, "", 0, "", emptyList()) - ).blockingGet() - - assertEquals(2, recipients.size) - assertEquals(1, recipients[0].studentId) - assertEquals("3rPracownik", recipients[1].realId) - assertEquals("Kowalski Jan", recipients[0].name) - assertEquals("Kowalska Karolina [KK] - Pracownik (Fake123456)", recipients[1].realName) - assertEquals(3, recipients[0].loginId) - assertEquals(4, recipients[1].unitId) - assertEquals(2, recipients[0].role) - assertEquals("hash", recipients[1].hash) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt deleted file mode 100644 index cecd80992..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.data.repositories.student - -import android.content.Context -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.SharedPrefHelper -import io.github.wulkanowy.data.db.entities.Student -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDateTime.now -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class StudentLocalTest { - - private lateinit var studentLocal: StudentLocal - - private lateinit var testDb: AppDatabase - - private lateinit var sharedHelper: SharedPrefHelper - - @Before - fun createDb() { - val context = ApplicationProvider.getApplicationContext() - testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) - .build() - sharedHelper = SharedPrefHelper(context.getSharedPreferences("TEST", Context.MODE_PRIVATE)) - studentLocal = StudentLocal(testDb.studentDao, context) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - studentLocal.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 deleted file mode 100644 index eea5463d1..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import io.github.wulkanowy.api.toDate -import io.github.wulkanowy.utils.toDate -import org.threeten.bp.LocalDateTime -import org.threeten.bp.LocalDateTime.now -import io.github.wulkanowy.api.timetable.Timetable as TimetableRemote -import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal - -fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", subject: String = "", 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 deleted file mode 100644 index fe25e4e96..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Semester -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDateTime.of -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class TimetableLocalTest { - - private lateinit var timetableDb: TimetableLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - timetableDb = TimetableLocal(testDb.timetableDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - timetableDb.saveTimetable(listOf( - createTimetableLocal(1, of(2018, 9, 10, 0, 0, 0)), - createTimetableLocal(1, of(2018, 9, 14, 0, 0, 0)), - createTimetableLocal(1, of(2018, 9, 17, 0, 0, 0)) - )) - - val exams = timetableDb.getTimetable( - Semester(1, 2, "", 1, 1, 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 deleted file mode 100644 index 79216d552..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import android.os.Build.VERSION_CODES.P -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider.getApplicationContext -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SdkSuppress -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.impl.annotations.SpyK -import io.reactivex.Single -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDateTime.of -import kotlin.test.assertEquals - -@SdkSuppress(minSdkVersion = P) -@RunWith(AndroidJUnit4::class) -class TimetableRepositoryTest { - - @SpyK - private var mockApi = Api() - - private val settings = InternetObservingSettings.builder() - .strategy(TestInternetObservingStrategy()) - .build() - - @MockK - private lateinit var semesterMock: Semester - - private lateinit var timetableRemote: TimetableRemote - - private lateinit var timetableLocal: TimetableLocal - - private lateinit var testDb: AppDatabase - - @Before - fun initApi() { - MockKAnnotations.init(this) - testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() - timetableLocal = TimetableLocal(testDb.timetableDao) - timetableRemote = TimetableRemote(mockApi) - - every { semesterMock.studentId } returns 1 - every { semesterMock.diaryId } returns 2 - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun copyDetailsToCompletedFromPrevious() { - timetableLocal.saveTimetable(listOf( - createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda"), - createTimetableLocal(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 new file mode 100644 index 000000000..2fd1904ba --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.java @@ -0,0 +1,25 @@ +package io.github.wulkanowy.utils.security; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SdkSuppress; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ScramblerTest { + + @Test + @SdkSuppress(minSdkVersion = 18) + public void encryptDecryptTest() throws Exception { + Context targetContext = InstrumentationRegistry.getTargetContext(); + + Assert.assertEquals("PASS", Scrambler.decrypt("TEST", + Scrambler.encrypt("TEST", "PASS", targetContext))); + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt b/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt deleted file mode 100644 index 0c47e6bb6..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/utils/security/ScramblerTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.wulkanowy.utils.security - -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SdkSuppress -import androidx.test.filters.SmallTest -import org.junit.Test -import org.junit.runner.RunWith -import java.security.KeyStore -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -class ScramblerTest { - - @Test - fun encryptDecryptTest() { - assertEquals("TEST", decrypt(encrypt("TEST", - ApplicationProvider.getApplicationContext()))) - } - - @Test - fun emptyTextEncryptTest() { - assertFailsWith { - decrypt("") - } - - assertFailsWith { - encrypt("", ApplicationProvider.getApplicationContext()) - } - } - - @Test - @SdkSuppress(minSdkVersion = 18) - fun emptyKeyStoreTest() { - val text = encrypt("test", ApplicationProvider.getApplicationContext()) - - val keyStore = KeyStore.getInstance("AndroidKeyStore") - keyStore.load(null) - keyStore.deleteEntry("wulkanowy_password") - - assertFailsWith { - decrypt(text) - } - } -} diff --git a/app/src/debug/google-services.json b/app/src/debug/google-services.json deleted file mode 100644 index e9303986b..000000000 --- a/app/src/debug/google-services.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "project_info": { - "project_number": "", - "firebase_url": "", - "project_id": "", - "storage_bucket": "" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:1091101852179:android:b558a25f65d088b1", - "android_client_info": { - "package_name": "io.github.wulkanowy.dev" - } - }, - "oauth_client": [ - { - "client_id": "", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "" - } - ], - "services": { - "analytics_service": { - "status": 1 - }, - "appinvite_service": { - "status": 1, - "other_platform_oauth_client": [] - }, - "ads_service": { - "status": 2 - } - } - } - ], - "configuration_version": "1" -} diff --git a/app/src/debug/google-services.json.gpg b/app/src/debug/google-services.json.gpg deleted file mode 100644 index 736f7906d..000000000 Binary files a/app/src/debug/google-services.json.gpg and /dev/null differ diff --git a/app/src/debug/res/drawable/ic_launcher_foreground.xml b/app/src/debug/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index 6be799094..000000000 --- a/app/src/debug/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 7353dbd1f..000000000 --- a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 7353dbd1f..000000000 --- a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher.png b/app/src/debug/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 5b688d7cb..000000000 Binary files a/app/src/debug/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 81e723ecc..000000000 Binary files a/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher.png b/app/src/debug/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 48b13240c..000000000 Binary files a/app/src/debug/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 394b57076..000000000 Binary files a/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index ff8bfa3e9..000000000 Binary files a/app/src/debug/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 365b4d663..000000000 Binary files a/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 96be1ed4f..000000000 Binary files a/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 463c089b3..000000000 Binary files a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 57c7416f1..000000000 Binary files a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 53d6f5bbd..000000000 Binary files a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/debug/res/values/ic_launcher_background.xml b/app/src/debug/res/values/ic_launcher_background.xml deleted file mode 100644 index 9646c0b4e..000000000 --- a/app/src/debug/res/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #D32F2F - \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2a214b8b2..dd982626a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,15 +1,12 @@ - + android:installLocation="internalOnly"> - + android:theme="@style/WulkanowyTheme"> + android:name=".ui.splash.SplashActivity" + android:configChanges="orientation|screenSize" + android:noHistory="true" + android:theme="@style/WulkanowyTheme.SplashTheme"> + + android:label="@string/activity_dashboard_text" + android:launchMode="singleTop" + /> + android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity" + android:theme="@style/WulkanowyTheme.DarkActionBar" /> - - - - - - - - - + android:name="com.google.android.gms.oss.licenses.OssLicensesActivity" + android:theme="@style/WulkanowyTheme.DarkActionBar" /> + + + + + - + + android:resource="@xml/widget_provider" /> - - - - - - - - - - + android:value="${fabricApiKey}" /> + diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png new file mode 100644 index 000000000..7cad5c2f4 Binary files /dev/null and b/app/src/main/ic_launcher-web.png differ diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.java b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.java new file mode 100644 index 000000000..d3570512a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.java @@ -0,0 +1,74 @@ +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 deleted file mode 100644 index a58a9804e..000000000 --- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt +++ /dev/null @@ -1,73 +0,0 @@ -package io.github.wulkanowy - -import android.content.Context -import androidx.multidex.MultiDex -import androidx.work.Configuration -import androidx.work.WorkManager -import com.crashlytics.android.Crashlytics -import com.crashlytics.android.core.CrashlyticsCore -import com.jakewharton.threetenabp.AndroidThreeTen -import dagger.android.AndroidInjector -import dagger.android.support.DaggerApplication -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.utils.Log -import io.fabric.sdk.android.Fabric -import io.github.wulkanowy.BuildConfig.CRASHLYTICS_ENABLED -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.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() - initLogging() - } - - private fun initLogging() { - if (DEBUG) { - Timber.plant(DebugLogTree()) - FlexibleAdapter.enableLogs(Log.Level.DEBUG) - } else { - Timber.plant(CrashlyticsTree()) - } - registerActivityLifecycleCallbacks(ActivityLifecycleLogger()) - } - - private fun initCrashlytics() { - Fabric.with(Fabric.Builder(this).kits( - Crashlytics.Builder().core(CrashlyticsCore.Builder().disabled(!CRASHLYTICS_ENABLED).build()).build() - ).debuggable(DEBUG).build()) - } - - 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 deleted file mode 100644 index b6eee316b..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.data - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Student -import java.net.URL -import javax.inject.Inject - -class ApiHelper @Inject constructor(private val api: Api) { - - fun initApi(student: Student) { - api.apply { - email = student.email - password = student.password - symbol = student.symbol - schoolSymbol = student.schoolSymbol - studentId = student.studentId - classId = student.classId - host = URL(student.endpoint).run { host + ":$port".removeSuffix(":-1") } - ssl = student.endpoint.startsWith("https") - loginType = Api.LoginType.valueOf(student.loginType) - useNewStudent = true - } - } - - fun initApi(email: String, password: String, symbol: String, endpoint: String) { - api.apply { - this.email = email - this.password = password - this.symbol = symbol - host = URL(endpoint).run { host + ":$port".removeSuffix(":-1") } - ssl = endpoint.startsWith("https") - useNewStudent = true - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/Repository.java b/app/src/main/java/io/github/wulkanowy/data/Repository.java new file mode 100644 index 000000000..ac6a8b42c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/Repository.java @@ -0,0 +1,56 @@ +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 new file mode 100644 index 000000000..e4dbd26b8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryContract.java @@ -0,0 +1,22 @@ +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 deleted file mode 100644 index c832368a5..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt +++ /dev/null @@ -1,135 +0,0 @@ -package io.github.wulkanowy.data - -import android.content.Context -import android.content.SharedPreferences -import android.content.res.Resources -import androidx.preference.PreferenceManager -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.WalledGardenInternetObservingStrategy -import com.readystatesoftware.chuck.api.ChuckCollector -import com.readystatesoftware.chuck.api.ChuckInterceptor -import com.readystatesoftware.chuck.api.RetentionManager -import dagger.Module -import dagger.Provides -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import okhttp3.logging.HttpLoggingInterceptor -import okhttp3.logging.HttpLoggingInterceptor.Level.BASIC -import okhttp3.logging.HttpLoggingInterceptor.Level.NONE -import timber.log.Timber -import javax.inject.Singleton - -@Module -internal class RepositoryModule { - - @Singleton - @Provides - fun provideInternetObservingSettings(): InternetObservingSettings { - return InternetObservingSettings.builder() - .strategy(WalledGardenInternetObservingStrategy()) - .build() - } - - @Singleton - @Provides - fun provideApi(chuckCollector: ChuckCollector, context: Context): Api { - return Api().apply { - logLevel = NONE - androidVersion = android.os.Build.VERSION.RELEASE - buildTag = android.os.Build.MODEL - setInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { Timber.d(it) }).setLevel(BASIC)) - - // for debug only - setInterceptor(ChuckInterceptor(context, chuckCollector).maxContentLength(250000L), true, 0) - } - } - - @Singleton - @Provides - fun provideChuckCollector(context: Context, prefRepository: PreferencesRepository): ChuckCollector { - return ChuckCollector(context) - .showNotification(prefRepository.isDebugNotificationEnable) - .retentionManager(RetentionManager(context, ChuckCollector.Period.ONE_HOUR)) - } - - @Singleton - @Provides - fun provideDatabase(context: Context) = AppDatabase.newInstance(context) - - @Singleton - @Provides - fun provideResources(context: Context): Resources = context.resources - - @Singleton - @Provides - fun provideSharedPref(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - - @Singleton - @Provides - fun provideStudentDao(database: AppDatabase) = database.studentDao - - @Singleton - @Provides - fun provideSemesterDao(database: AppDatabase) = database.semesterDao - - @Singleton - @Provides - fun provideGradeDao(database: AppDatabase) = database.gradeDao - - @Singleton - @Provides - fun provideGradeSummaryDao(database: AppDatabase) = database.gradeSummaryDao - - @Singleton - @Provides - fun provideGradeStatisticsDao(database: AppDatabase) = database.gradeStatistics - - @Singleton - @Provides - fun provideMessagesDao(database: AppDatabase) = database.messagesDao - - @Singleton - @Provides - fun provideExamDao(database: AppDatabase) = database.examsDao - - @Singleton - @Provides - fun provideAttendanceDao(database: AppDatabase) = database.attendanceDao - - @Singleton - @Provides - fun provideAttendanceSummaryDao(database: AppDatabase) = database.attendanceSummaryDao - - @Singleton - @Provides - fun provideTimetableDao(database: AppDatabase) = database.timetableDao - - @Singleton - @Provides - fun provideNoteDao(database: AppDatabase) = database.noteDao - - @Singleton - @Provides - fun provideHomeworkDao(database: AppDatabase) = database.homeworkDao - - @Singleton - @Provides - fun provideSubjectDao(database: AppDatabase) = database.subjectDao - - @Singleton - @Provides - fun provideLuckyNumberDao(database: AppDatabase) = database.luckyNumberDao - - @Singleton - @Provides - fun provideCompletedLessonsDao(database: AppDatabase) = database.completedLessonsDao - - @Singleton - @Provides - fun provideReportingUnitDao(database: AppDatabase) = database.reportingUnitDao - - @Singleton - @Provides - fun provideRecipientDao(database: AppDatabase) = database.recipientDao -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt deleted file mode 100644 index d867583e4..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ /dev/null @@ -1,143 +0,0 @@ -package io.github.wulkanowy.data.db - -import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import androidx.room.RoomDatabase.JournalMode.TRUNCATE -import androidx.room.TypeConverters -import io.github.wulkanowy.data.db.dao.AttendanceDao -import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao -import io.github.wulkanowy.data.db.dao.CompletedLessonsDao -import io.github.wulkanowy.data.db.dao.ExamDao -import io.github.wulkanowy.data.db.dao.GradeDao -import io.github.wulkanowy.data.db.dao.GradeStatisticsDao -import io.github.wulkanowy.data.db.dao.GradeSummaryDao -import io.github.wulkanowy.data.db.dao.HomeworkDao -import io.github.wulkanowy.data.db.dao.LuckyNumberDao -import io.github.wulkanowy.data.db.dao.MessagesDao -import io.github.wulkanowy.data.db.dao.NoteDao -import io.github.wulkanowy.data.db.dao.RecipientDao -import io.github.wulkanowy.data.db.dao.ReportingUnitDao -import io.github.wulkanowy.data.db.dao.SemesterDao -import io.github.wulkanowy.data.db.dao.StudentDao -import io.github.wulkanowy.data.db.dao.SubjectDao -import io.github.wulkanowy.data.db.dao.TimetableDao -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.db.entities.Subject -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.data.db.migrations.Migration10 -import io.github.wulkanowy.data.db.migrations.Migration11 -import io.github.wulkanowy.data.db.migrations.Migration12 -import io.github.wulkanowy.data.db.migrations.Migration13 -import io.github.wulkanowy.data.db.migrations.Migration2 -import io.github.wulkanowy.data.db.migrations.Migration3 -import io.github.wulkanowy.data.db.migrations.Migration4 -import io.github.wulkanowy.data.db.migrations.Migration5 -import io.github.wulkanowy.data.db.migrations.Migration6 -import io.github.wulkanowy.data.db.migrations.Migration7 -import io.github.wulkanowy.data.db.migrations.Migration8 -import io.github.wulkanowy.data.db.migrations.Migration9 -import javax.inject.Singleton - -@Singleton -@Database( - entities = [ - Student::class, - Semester::class, - Exam::class, - Timetable::class, - Attendance::class, - AttendanceSummary::class, - Grade::class, - GradeSummary::class, - GradeStatistics::class, - Message::class, - Note::class, - Homework::class, - Subject::class, - LuckyNumber::class, - CompletedLesson::class, - ReportingUnit::class, - Recipient::class - ], - version = AppDatabase.VERSION_SCHEMA, - exportSchema = true -) -@TypeConverters(Converters::class) -abstract class AppDatabase : RoomDatabase() { - - companion object { - const val VERSION_SCHEMA = 13 - - 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() - ) - .build() - } - } - - abstract val studentDao: StudentDao - - abstract val semesterDao: SemesterDao - - abstract val examsDao: ExamDao - - abstract val timetableDao: TimetableDao - - abstract val attendanceDao: AttendanceDao - - abstract val attendanceSummaryDao: AttendanceSummaryDao - - abstract val gradeDao: GradeDao - - abstract val gradeSummaryDao: GradeSummaryDao - - abstract val gradeStatistics: GradeStatisticsDao - - abstract val messagesDao: MessagesDao - - abstract val noteDao: NoteDao - - abstract val homeworkDao: HomeworkDao - - abstract val subjectDao: SubjectDao - - abstract val luckyNumberDao: LuckyNumberDao - - abstract val completedLessonsDao: CompletedLessonsDao - - abstract val reportingUnitDao: ReportingUnitDao - - abstract val recipientDao: RecipientDao -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt deleted file mode 100644 index 73a04d236..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt +++ /dev/null @@ -1,51 +0,0 @@ -package io.github.wulkanowy.data.db - -import androidx.room.TypeConverter -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken -import org.threeten.bp.DateTimeUtils -import org.threeten.bp.Instant -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDateTime -import org.threeten.bp.Month -import org.threeten.bp.ZoneOffset -import java.util.Date - -class Converters { - - @TypeConverter - fun timestampToDate(value: Long?): LocalDate? = value?.run { - DateTimeUtils.toInstant(Date(value)).atZone(ZoneOffset.UTC).toLocalDate() - } - - @TypeConverter - fun dateToTimestamp(date: LocalDate?): Long? { - return date?.atStartOfDay()?.toInstant(ZoneOffset.UTC)?.toEpochMilli() - } - - @TypeConverter - fun timestampToTime(value: Long?): LocalDateTime? = value?.let { - LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.UTC) - } - - @TypeConverter - fun timeToTimestamp(date: LocalDateTime?): Long? { - return date?.atZone(ZoneOffset.UTC)?.toInstant()?.toEpochMilli() - } - - @TypeConverter - fun monthToInt(month: Month?) = month?.value - - @TypeConverter - fun intToMonth(value: Int?) = value?.let { Month.of(it) } - - @TypeConverter - fun intListToGson(list: List): String { - return Gson().toJson(list) - } - - @TypeConverter - fun gsonToIntList(value: String): List { - return Gson().fromJson(value, object : TypeToken>() {}.type) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt deleted file mode 100644 index 74f9fa654..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.data.db - -import android.annotation.SuppressLint -import android.content.SharedPreferences -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -@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 deleted file mode 100644 index d3c4f146f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Attendance -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Singleton - -@Singleton -@Dao -interface AttendanceDao { - - @Insert - fun insertAll(exams: List): List - - @Delete - fun deleteAll(exams: List) - - @Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") - fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt deleted file mode 100644 index a7413de56..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceSummaryDao.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.reactivex.Maybe - -@Dao -interface AttendanceSummaryDao { - - @Insert - fun insertAll(exams: List): List - - @Delete - fun deleteAll(exams: List) - - @Query("SELECT * FROM AttendanceSummary WHERE diary_id = :diaryId AND student_id = :studentId AND subject_id = :subjectId") - fun loadAll(diaryId: Int, studentId: Int, subjectId: Int): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt deleted file mode 100644 index 6816ceaaf..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Singleton - -@Singleton -@Dao -interface CompletedLessonsDao { - - @Insert - fun insertAll(exams: List) - - @Delete - fun deleteAll(exams: List) - - @Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") - fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/DbContract.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbContract.java new file mode 100644 index 000000000..22eaa9445 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbContract.java @@ -0,0 +1,37 @@ +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 new file mode 100644 index 000000000..33b193115 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbHelper.java @@ -0,0 +1,102 @@ +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 new file mode 100644 index 000000000..d869bfe9a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/DbRepository.java @@ -0,0 +1,132 @@ +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 deleted file mode 100644 index 06cd56135..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Exam -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Singleton - -@Singleton -@Dao -interface ExamDao { - - @Insert - fun insertAll(exams: List): List - - @Delete - fun deleteAll(exams: List) - - @Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") - fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt deleted file mode 100644 index 0bd210b02..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeDao.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import androidx.room.Update -import io.github.wulkanowy.data.db.entities.Grade -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface GradeDao { - - @Insert - fun insertAll(grades: List) - - @Update - fun updateAll(grade: List) - - @Delete - fun deleteAll(grades: List) - - @Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId") - fun loadAll(semesterId: Int, studentId: Int): Maybe> - -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt deleted file mode 100644 index 338c369fa..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface GradeStatisticsDao { - - @Insert - fun insertAll(gradesStatistics: List) - - @Delete - fun deleteAll(gradesStatistics: List) - - @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester") - fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): Maybe> - - @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND is_semester = :isSemester") - fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt deleted file mode 100644 index 3f2e87bd0..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSummaryDao.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface GradeSummaryDao { - - @Insert - fun insertAll(gradesSummary: List) - - @Delete - fun deleteAll(gradesSummary: List) - - @Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId") - fun loadAll(semesterId: Int, studentId: Int): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt deleted file mode 100644 index 253bdb11f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/HomeworkDao.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Homework -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Singleton - -@Singleton -@Dao -interface HomeworkDao { - - @Insert - fun insertAll(homework: List) - - @Delete - fun deleteAll(homework: List) - - @Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date >= :from AND date <= :end") - fun loadAll(semesterId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt deleted file mode 100644 index afd7905c0..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/LuckyNumberDao.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import androidx.room.Update -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Singleton - -@Singleton -@Dao -interface LuckyNumberDao { - - @Insert - fun insert(luckyNumber: LuckyNumber) - - @Update - fun update(luckyNumber: LuckyNumber) - - @Delete - fun delete(luckyNumber: LuckyNumber) - - @Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date") - fun load(studentId: Int, date: LocalDate): Maybe - -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt deleted file mode 100644 index 4f72c6c91..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import androidx.room.Update -import io.github.wulkanowy.data.db.entities.Message -import io.reactivex.Maybe - -@Dao -interface MessagesDao { - - @Insert - fun insertAll(messages: List) - - @Delete - fun deleteAll(messages: List) - - @Update - fun updateAll(messages: List) - - @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC") - fun loadAll(studentId: Int, folder: Int): Maybe> - - @Query("SELECT * FROM Messages WHERE 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/NoteDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt deleted file mode 100644 index 867e06a25..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/NoteDao.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import androidx.room.Update -import io.github.wulkanowy.data.db.entities.Note -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface NoteDao { - - @Insert - fun insertAll(notes: List) - - @Update - fun updateAll(notes: List) - - @Delete - fun deleteAll(notes: List) - - @Query("SELECT * FROM Notes WHERE student_id = :studentId") - fun loadAll(studentId: Int): Maybe> - -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt deleted file mode 100644 index 7c5fd6ca6..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Recipient -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface RecipientDao { - - @Insert - fun insertAll(messages: List) - - @Delete - fun deleteAll(messages: List) - - @Query("SELECT * FROM Recipients WHERE student_id = :studentId AND role = :role AND unit_id = :unitId") - fun load(studentId: Int, role: Int, unitId: Int): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt deleted file mode 100644 index 1898390a9..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface ReportingUnitDao { - - @Insert - fun insertAll(reportingUnits: List) - - @Delete - fun deleteAll(reportingUnits: List) - - @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId") - fun load(studentId: Int): Maybe> - - @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId") - fun loadOne(studentId: Int, unitId: Int): Maybe -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt deleted file mode 100644 index 01841fb67..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface SemesterDao { - - @Insert - fun insertAll(semester: List) - - @Delete - fun deleteAll(semester: List) - - @Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId") - fun loadAll(studentId: Int, classId: Int): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt deleted file mode 100644 index 57bf25fb8..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy.ABORT -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Maybe -import javax.inject.Singleton - -@Singleton -@Dao -interface StudentDao { - - @Insert(onConflict = ABORT) - fun 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 deleted file mode 100644 index 725a371ab..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SubjectDao.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Subject -import io.reactivex.Maybe - -@Dao -interface SubjectDao { - - @Insert - fun insertAll(subjects: List): List - - @Delete - fun deleteAll(subjects: List) - - @Query("SELECT * FROM Subjects WHERE diary_id = :diaryId AND student_id = :studentId") - fun loadAll(diaryId: Int, studentId: Int): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt deleted file mode 100644 index abe213618..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Timetable -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Singleton - -@Singleton -@Dao -interface TimetableDao { - - @Insert - fun insertAll(exams: List): List - - @Delete - fun deleteAll(exams: List) - - @Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") - fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java new file mode 100644 index 000000000..78dff3610 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Account.java @@ -0,0 +1,153 @@ +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 new file mode 100644 index 000000000..81235e596 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/AttendanceLesson.java @@ -0,0 +1,254 @@ +package io.github.wulkanowy.data.db.dao.entities; + +import org.greenrobot.greendao.DaoException; +import org.greenrobot.greendao.annotation.Entity; +import org.greenrobot.greendao.annotation.Generated; +import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.Index; +import org.greenrobot.greendao.annotation.Property; +import org.greenrobot.greendao.annotation.Transient; + +import java.io.Serializable; + +@Entity( + nameInDb = "AttendanceLessons", + active = true, + indexes = {@Index(value = "dayId,date,number", unique = true)} +) +public class AttendanceLesson implements Serializable { + + @Id(autoincrement = true) + private Long id; + + @Property(nameInDb = "day_id") + private Long dayId; + + @Property(nameInDb = "date") + private String date = ""; + + @Property(nameInDb = "number_of_lesson") + private int number = 0; + + @Property(nameInDb = "subject") + 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 new file mode 100644 index 000000000..c5472e989 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Day.java @@ -0,0 +1,263 @@ +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 new file mode 100644 index 000000000..b0b145daa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Diary.java @@ -0,0 +1,177 @@ +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 new file mode 100644 index 000000000..543b68597 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Exam.java @@ -0,0 +1,177 @@ +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 new file mode 100644 index 000000000..537b9e751 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Grade.java @@ -0,0 +1,293 @@ +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 new file mode 100644 index 000000000..59860e9eb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/School.java @@ -0,0 +1,179 @@ +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 new file mode 100644 index 000000000..c739688f6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Semester.java @@ -0,0 +1,208 @@ +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 new file mode 100644 index 000000000..1d5454732 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Student.java @@ -0,0 +1,178 @@ +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 new file mode 100644 index 000000000..285cd7c06 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Subject.java @@ -0,0 +1,176 @@ +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 new file mode 100644 index 000000000..7cc461112 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Symbol.java @@ -0,0 +1,175 @@ +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 new file mode 100644 index 000000000..f3fdf6a83 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/TimetableLesson.java @@ -0,0 +1,332 @@ +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 new file mode 100644 index 000000000..1edfa2454 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/entities/Week.java @@ -0,0 +1,194 @@ +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 new file mode 100644 index 000000000..b2a41ad15 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration23.java @@ -0,0 +1,114 @@ +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 new file mode 100644 index 000000000..332f2dd27 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration26.java @@ -0,0 +1,20 @@ +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 new file mode 100644 index 000000000..922079f40 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration27.java @@ -0,0 +1,24 @@ +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 new file mode 100644 index 000000000..3970df4cf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration28.java @@ -0,0 +1,20 @@ +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 new file mode 100644 index 000000000..e318b5fd0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/migrations/Migration29.kt @@ -0,0 +1,60 @@ +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 deleted file mode 100644 index 3c58971ae..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "Attendance") -data class Attendance( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "diary_id") - val diaryId: Int, - - val date: LocalDate, - - val number: Int, - - val subject: String, - - val name: String, - - val presence: Boolean, - - val absence: Boolean, - - val exemption: Boolean, - - val lateness: Boolean, - - val excused: Boolean, - - val deleted: Boolean -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt deleted file mode 100644 index d2e1f174e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/AttendanceSummary.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.Month -import java.io.Serializable - -@Entity(tableName = "AttendanceSummary") -data class AttendanceSummary( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "diary_id") - val diaryId: Int, - - @ColumnInfo(name = "subject_id") - val subjectId: Int, - - val month: Month, - - val presence: Int, - - val absence: Int, - - @ColumnInfo(name = "absence_excused") - val absenceExcused: Int, - - @ColumnInfo(name = "absence_for_school_reasons") - val absenceForSchoolReasons: Int, - - val lateness: Int, - - @ColumnInfo(name = "lateness_excused") - val latenessExcused: Int, - - val exemption: Int -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt deleted file mode 100644 index 775f3f558..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/CompletedLesson.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "CompletedLesson") -data class CompletedLesson( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "diary_id") - val diaryId: Int, - - val date: LocalDate, - - val number: Int, - - val subject: String, - - val topic: String, - - val teacher: String, - - @ColumnInfo(name = "teacher_symbol") - val teacherSymbol: String, - - val substitution: String, - - val absence: String, - - val resources: String -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt deleted file mode 100644 index 9ae795e71..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "Exams") -data class Exam( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "diary_id") - val diaryId: Int, - - val date: LocalDate, - - @ColumnInfo(name = "entry_date") - val entryDate: LocalDate, - - val subject: String, - - val group: String, - - val type: String, - - val description: String, - - val teacher: String, - - @ColumnInfo(name = "teacher_symbol") - val teacherSymbol: String -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt deleted file mode 100644 index 1221a7aab..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "Grades") -data class Grade( - - @ColumnInfo(name = "semester_id") - val semesterId: Int, - - @ColumnInfo(name = "student_id") - val studentId: Int, - - val subject: String, - - val entry: String, - - val value: Int, - - val modifier: Double, - - val comment: String, - - val color: String, - - @ColumnInfo(name = "grade_symbol") - val gradeSymbol: String, - - val description: String, - - val weight: String, - - val weightValue: Double, - - val date: LocalDate, - - val teacher: String -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 - - @ColumnInfo(name = "is_read") - var isRead: Boolean = true - - @ColumnInfo(name = "is_notified") - var isNotified: Boolean = true -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt deleted file mode 100644 index 8ad8b8b8d..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity(tableName = "GradesStatistics") -data class GradeStatistics( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "semester_id") - val semesterId: Int, - - val subject: String, - - val grade: Int, - - val amount: Int, - - @ColumnInfo(name = "is_semester") - val semester: Boolean -) { - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt deleted file mode 100644 index e6ac4926d..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity(tableName = "GradesSummary") -data class GradeSummary( - - @ColumnInfo(name = "semester_id") - val semesterId: Int, - - @ColumnInfo(name = "student_id") - val studentId: Int, - - val subject: String, - - val predictedGrade: String, - - val finalGrade: String -) { - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt deleted file mode 100644 index a22df0961..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "Homework") -data class Homework( - - @ColumnInfo(name = "semester_id") - val semesterId: Int, - - @ColumnInfo(name = "student_id") - val studentId: Int, - - val date: LocalDate, - - @ColumnInfo(name = "entry_date") - val entryDate: LocalDate, - - val subject: String, - - val content: String, - - val teacher: String, - - @ColumnInfo(name = "teacher_symbol") - val teacherSymbol: String - -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt deleted file mode 100644 index 5b9130f5d..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/LuckyNumber.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "LuckyNumbers") -data class LuckyNumber ( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - val date: LocalDate, - - @ColumnInfo(name = "lucky_number") - val luckyNumber: Int - -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 - - @ColumnInfo(name = "is_notified") - var isNotified: Boolean = true -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt deleted file mode 100644 index 48b4fd022..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDateTime -import java.io.Serializable - -@Entity(tableName = "Messages") -data class Message( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "real_id") - val realId: Int, - - @ColumnInfo(name = "message_id") - val messageId: Int, - - @ColumnInfo(name = "sender_name") - val sender: String, - - @ColumnInfo(name = "sender_id") - val senderId: Int, - - @ColumnInfo(name = "recipient_name") - val recipient: String, - - val subject: String, - - val date: LocalDateTime, - - @ColumnInfo(name = "folder_id") - val folderId: Int, - - var unread: Boolean, - - @ColumnInfo(name = "unread_by") - val unreadBy: Int, - - @ColumnInfo(name = "read_by") - val readBy: Int, - - val removed: Boolean -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 - - @ColumnInfo(name = "is_notified") - var isNotified: Boolean = true - - var content: String? = null -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt deleted file mode 100644 index 5f3a92ab6..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import java.io.Serializable - -@Entity(tableName = "Notes") -data class Note( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - val date: LocalDate, - - val teacher: String, - - val category: String, - - val content: String -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 - - @ColumnInfo(name = "is_read") - var isRead: Boolean = true - - @ColumnInfo(name = "is_notified") - var isNotified: Boolean = true -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt deleted file mode 100644 index 3021da72d..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import java.io.Serializable - -@Entity(tableName = "Recipients") -data class Recipient( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "real_id") - val realId: String, - - val name: String, - - @ColumnInfo(name = "real_name") - val realName: String, - - @ColumnInfo(name = "login_id") - val loginId: Int, - - @ColumnInfo(name = "unit_id") - val unitId: Int, - - val role: Int, - - val hash: String - -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 - - override fun toString() = name -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt deleted file mode 100644 index 601d8aac7..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import java.io.Serializable - -@Entity(tableName = "ReportingUnits") -data class ReportingUnit( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "real_id") - val realId: Int, - - @ColumnInfo(name = "short") - val shortName: String, - - @ColumnInfo(name = "sender_id") - val senderId: Int, - - @ColumnInfo(name = "sender_name") - val senderName: String, - - val roles: List - -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt deleted file mode 100644 index 6c06be111..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.Index -import androidx.room.PrimaryKey -import org.threeten.bp.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 deleted file mode 100644 index 13c5ee084..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.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 deleted file mode 100644 index dbaa6f4ed..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Subject.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import java.io.Serializable - -@Entity(tableName = "Subjects") -data class Subject( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "diary_id") - val diaryId: Int, - - @ColumnInfo(name = "real_id") - val realId: Int, - - val name: String -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt deleted file mode 100644 index 9bc3d2140..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDateTime -import java.io.Serializable - -@Entity(tableName = "Timetable") -data class Timetable( - - @ColumnInfo(name = "student_id") - val studentId: Int, - - @ColumnInfo(name = "diary_id") - val diaryId: Int, - - val number: Int, - - val start: LocalDateTime, - - val end: LocalDateTime, - - val date: LocalDate, - - val subject: String, - - val subjectOld: String, - - val group: String, - - val room: String, - - val roomOld: String, - - val teacher: String, - - val teacherOld: String, - - val info: String, - - val changes: Boolean, - - val canceled: Boolean -) : Serializable { - - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt deleted file mode 100644 index c26a02d1f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration10 : Migration(9, 10) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE Grades_Summary RENAME TO GradesSummary") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt deleted file mode 100644 index 6d129bca0..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration11 : Migration(10, 11) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL(""" - CREATE TABLE IF NOT EXISTS Grades_temp ( - id INTEGER PRIMARY KEY NOT NULL, - is_read INTEGER NOT NULL, - is_notified INTEGER NOT NULL, - semester_id INTEGER NOT NULL, - student_id INTEGER NOT NULL, - subject TEXT NOT NULL, - entry TEXT NOT NULL, - value INTEGER NOT NULL, - modifier REAL NOT NULL, - comment TEXT NOT NULL, - color TEXT NOT NULL, - grade_symbol TEXT NOT NULL, - description TEXT NOT NULL, - weight TEXT NOT NULL, - weightValue REAL NOT NULL, - date INTEGER NOT NULL, - teacher TEXT NOT NULL - ) - """) - database.execSQL("INSERT INTO Grades_temp SELECT * FROM Grades") - database.execSQL("DROP TABLE Grades") - database.execSQL("ALTER TABLE Grades_temp RENAME TO Grades") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt deleted file mode 100644 index 1dc38e14c..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt +++ /dev/null @@ -1,69 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration12 : Migration(11, 12) { - - override fun migrate(database: SupportSQLiteDatabase) { - createTempStudentsTable(database) - replaceStudentTable(database) - updateStudentsWithClassId(database, getStudentsIds(database)) - removeStudentsWithNoClassId(database) - ensureThereIsOnlyOneCurrentStudent(database) - } - - private fun createTempStudentsTable(database: SupportSQLiteDatabase) { - database.execSQL(""" - CREATE TABLE IF NOT EXISTS Students_tmp ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - endpoint TEXT NOT NULL, - loginType TEXT NOT NULL, - email TEXT NOT NULL, - password TEXT NOT NULL, - symbol TEXT NOT NULL, - student_id INTEGER NOT NULL, - student_name TEXT NOT NULL, - school_id TEXT NOT NULL, - school_name TEXT NOT NULL, - is_current INTEGER NOT NULL, - registration_date INTEGER NOT NULL, - class_id INTEGER NOT NULL - ) - """) - database.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students_tmp (email, symbol, student_id, school_id, class_id)") - } - - private fun replaceStudentTable(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE Students ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") - database.execSQL("INSERT INTO Students_tmp SELECT * FROM Students") - database.execSQL("DROP TABLE Students") - database.execSQL("ALTER TABLE Students_tmp RENAME TO Students") - } - - private fun getStudentsIds(database: SupportSQLiteDatabase): List { - val students = mutableListOf() - val studentsCursor = database.query("SELECT student_id FROM Students") - if (studentsCursor.moveToFirst()) { - do { - students.add(studentsCursor.getInt(0)) - } while (studentsCursor.moveToNext()) - } - return students - } - - private fun updateStudentsWithClassId(database: SupportSQLiteDatabase, students: List) { - students.forEach { - database.execSQL("UPDATE Students SET class_id = IFNULL((SELECT class_id FROM Semesters WHERE student_id = $it), 0) WHERE student_id = $it") - } - } - - private fun removeStudentsWithNoClassId(database: SupportSQLiteDatabase) { - database.execSQL("DELETE FROM Students WHERE class_id = 0") - } - - private fun ensureThereIsOnlyOneCurrentStudent(database: SupportSQLiteDatabase) { - database.execSQL("UPDATE Students SET is_current = 0") - database.execSQL("UPDATE Students SET is_current = 1 WHERE id = (SELECT MAX(id) FROM Students)") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt deleted file mode 100644 index 0cf8cd9b0..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt +++ /dev/null @@ -1,64 +0,0 @@ -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/Migration2.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt deleted file mode 100644 index c5a30991a..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration2 : Migration(1, 2) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL(""" - CREATE TABLE IF NOT EXISTS LuckyNumbers ( - id INTEGER PRIMARY KEY NOT NULL, - is_notified INTEGER NOT NULL, - student_id INTEGER NOT NULL, - date INTEGER NOT NULL, - lucky_number INTEGER NOT NULL) - """) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt deleted file mode 100644 index d9699c0f4..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration3 : Migration(2, 3) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL(""" - CREATE TABLE IF NOT EXISTS CompletedLesson ( - id INTEGER PRIMARY KEY NOT NULL, - student_id INTEGER NOT NULL, - diary_id INTEGER NOT NULL, - date INTEGER NOT NULL, - number INTEGER NOT NULL, - subject TEXT NOT NULL, - topic TEXT NOT NULL, - teacher TEXT NOT NULL, - teacher_symbol TEXT NOT NULL, - substitution TEXT NOT NULL, - absence TEXT NOT NULL, - resources TEXT NOT NULL) - """) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt deleted file mode 100644 index 0ae89bdd6..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration4 : Migration(3, 4) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS Messages") - database.execSQL(""" - CREATE TABLE IF NOT EXISTS Messages ( - id INTEGER PRIMARY KEY NOT NULL, - is_notified INTEGER NOT NULL, - content TEXT, - student_id INTEGER NOT NULL, - real_id INTEGER NOT NULL, - message_id INTEGER NOT NULL, - sender_name TEXT NOT NULL, - sender_id INTEGER NOT NULL, - recipient_id INTEGER NOT NULL, - recipient_name TEXT NOT NULL, - subject TEXT NOT NULL, - date INTEGER NOT NULL, - folder_id INTEGER NOT NULL, - unread INTEGER NOT NULL, - unreadBy INTEGER NOT NULL, - readBy INTEGER NOT NULL, - removed INTEGER NOT NULL) - """) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt deleted file mode 100644 index fe0dec48f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase -import org.threeten.bp.LocalDateTime.now -import org.threeten.bp.ZoneOffset - -class Migration5 : Migration(4, 5) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE Students ADD COLUMN registration_date INTEGER DEFAULT 0 NOT NULL") - database.execSQL("UPDATE Students SET registration_date = '${now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli()}'") - database.execSQL("DROP TABLE IF EXISTS Notes") - database.execSQL(""" - CREATE TABLE IF NOT EXISTS Notes ( - id INTEGER PRIMARY KEY NOT NULL, - is_read INTEGER NOT NULL, - is_notified INTEGER NOT NULL, - student_id INTEGER NOT NULL, - date INTEGER NOT NULL, - teacher TEXT NOT NULL, - category TEXT NOT NULL, - content TEXT NOT NULL) - """) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt deleted file mode 100644 index fa9436187..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration6 : Migration(5, 6) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL(""" - CREATE TABLE IF NOT EXISTS ReportingUnits ( - id INTEGER PRIMARY KEY NOT NULL, - student_id INTEGER NOT NULL, - real_id INTEGER NOT NULL, - short TEXT NOT NULL, - sender_id INTEGER NOT NULL, - sender_name TEXT NOT NULL, - roles TEXT NOT NULL) - """) - - database.execSQL(""" - CREATE TABLE IF NOT EXISTS Recipients ( - id INTEGER PRIMARY KEY NOT NULL, - student_id INTEGER NOT NULL, - real_id TEXT NOT NULL, - name TEXT NOT NULL, - real_name TEXT NOT NULL, - login_id INTEGER NOT NULL, - unit_id INTEGER NOT NULL, - role INTEGER NOT NULL, - hash TEXT NOT NULL) - """) - - database.execSQL("DELETE FROM Semesters WHERE 1") - database.execSQL("ALTER TABLE Semesters ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") - database.execSQL("ALTER TABLE Semesters ADD COLUMN unit_id INTEGER DEFAULT 0 NOT NULL") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt deleted file mode 100644 index 120716c81..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration7 : Migration(6, 7) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL(""" - CREATE TABLE IF NOT EXISTS GradesStatistics ( - id INTEGER PRIMARY KEY NOT NULL, - student_id INTEGER NOT NULL, - semester_id INTEGER NOT NULL, - subject TEXT NOT NULL, - grade INTEGER NOT NULL, - amount INTEGER NOT NULL, - is_semester INTEGER NOT NULL) - """) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt deleted file mode 100644 index 7009ee129..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration8 : Migration(7, 8) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE Timetable ADD COLUMN subjectOld TEXT DEFAULT \"\" NOT NULL") - database.execSQL("ALTER TABLE Timetable ADD COLUMN roomOld TEXT DEFAULT \"\" NOT NULL") - database.execSQL("ALTER TABLE Timetable ADD COLUMN teacherOld TEXT DEFAULT \"\" NOT NULL") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt deleted file mode 100644 index d79a57062..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration9 : Migration(8, 9) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS Messages") - database.execSQL(""" - CREATE TABLE IF NOT EXISTS Messages ( - id INTEGER PRIMARY KEY NOT NULL, - student_id INTEGER NOT NULL, - real_id INTEGER NOT NULL, - message_id INTEGER NOT NULL, - sender_name TEXT NOT NULL, - sender_id INTEGER NOT NULL, - recipient_name TEXT NOT NULL, - subject TEXT NOT NULL, - date INTEGER NOT NULL, - folder_id INTEGER NOT NULL, - unread INTEGER NOT NULL, - unread_by INTEGER NOT NULL, - read_by INTEGER NOT NULL, - removed INTEGER NOT NULL, - is_notified INTEGER NOT NULL, - content TEXT) - """) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java new file mode 100644 index 000000000..0e654997b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesContract.java @@ -0,0 +1,17 @@ +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 new file mode 100644 index 000000000..f8a1baaa2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/resources/ResourcesRepository.java @@ -0,0 +1,87 @@ +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 new file mode 100644 index 000000000..370f8dfe1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefContract.java @@ -0,0 +1,35 @@ +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 new file mode 100644 index 000000000..adedbda4e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/shared/SharedPrefRepository.java @@ -0,0 +1,102 @@ +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 deleted file mode 100644 index 58a2396e2..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt +++ /dev/null @@ -1,3 +0,0 @@ -package io.github.wulkanowy.data.exceptions - -class NoCurrentStudentException : Exception("There no set current student in database") diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt deleted file mode 100644 index 0f5873766..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendance - -import io.github.wulkanowy.data.db.dao.AttendanceDao -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDao) { - - fun saveAttendance(attendance: List) { - attendanceDb.insertAll(attendance) - } - - fun deleteAttendance(attendance: List) { - attendanceDb.deleteAll(attendance) - } - - fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { - return attendanceDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt deleted file mode 100644 index b3544c3f5..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendance - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate -import io.reactivex.Single -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceRemote @Inject constructor(private val api: Api) { - - fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getAttendance(startDate, endDate) }.map { attendance -> - attendance.map { - Attendance( - studentId = semester.studentId, - diaryId = semester.diaryId, - date = it.date.toLocalDate(), - number = it.number, - subject = it.subject, - name = it.name, - presence = it.presence, - absence = it.absence, - exemption = it.exemption, - lateness = it.lateness, - excused = it.excused, - deleted = it.deleted - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt deleted file mode 100644 index 85102b3c5..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendance - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.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 deleted file mode 100644 index 2e9a10067..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryLocal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendancesummary - -import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceSummaryLocal @Inject constructor(private val attendanceDb: AttendanceSummaryDao) { - - fun saveAttendanceSummary(attendance: List) { - attendanceDb.insertAll(attendance) - } - - fun deleteAttendanceSummary(attendance: List) { - attendanceDb.deleteAll(attendance) - } - - fun getAttendanceSummary(semester: Semester, subjectId: Int): Maybe> { - return attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt deleted file mode 100644 index d38dd3a4b..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendancesummary - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceSummaryRemote @Inject constructor(private val api: Api) { - - fun getAttendanceSummary(semester: Semester, subjectId: Int): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { api.getAttendanceSummary(subjectId) }.map { attendance -> - attendance.map { - AttendanceSummary( - studentId = semester.studentId, - diaryId = semester.diaryId, - subjectId = subjectId, - month = it.month, - presence = it.presence, - absence = it.absence, - absenceExcused = it.absenceExcused, - absenceForSchoolReasons = it.absenceForSchoolReasons, - lateness = it.lateness, - latenessExcused = it.latenessExcused, - exemption = it.exemption - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt deleted file mode 100644 index c65870508..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendancesummary - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.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 deleted file mode 100644 index 9b275908e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.completedlessons - -import io.github.wulkanowy.data.db.dao.CompletedLessonsDao -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class CompletedLessonsLocal @Inject constructor(private val completedLessonsDb: CompletedLessonsDao) { - - fun saveCompletedLessons(completedLessons: List) { - completedLessonsDb.insertAll(completedLessons) - } - - fun deleteCompleteLessons(completedLessons: List) { - completedLessonsDb.deleteAll(completedLessons) - } - - fun getCompletedLessons(semester: Semester, start: LocalDate, end: LocalDate): Maybe> { - return completedLessonsDb.loadAll(semester.diaryId, semester.studentId, start, end).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt deleted file mode 100644 index 58dd5a9d1..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.data.repositories.completedlessons - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.api.toLocalDate -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Single -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class CompletedLessonsRemote @Inject constructor(private val api: Api) { - - fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getCompletedLessons(startDate, endDate) } - .map { lessons -> - lessons.map { - it.absence - CompletedLesson( - studentId = semester.studentId, - diaryId = semester.diaryId, - date = it.date.toLocalDate(), - number = it.number, - subject = it.subject, - topic = it.topic, - teacher = it.teacher, - teacherSymbol = it.teacherSymbol, - substitution = it.substitution, - absence = it.absence, - resources = it.resources - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt deleted file mode 100644 index c22fabc39..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.wulkanowy.data.repositories.completedlessons - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.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 deleted file mode 100644 index 0f484d323..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamLocal.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.repositories.exam - -import io.github.wulkanowy.data.db.dao.ExamDao -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ExamLocal @Inject constructor(private val examDb: ExamDao) { - - fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { - return examDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate) - .filter { !it.isEmpty() } - } - - fun saveExams(exams: List) { - examDb.insertAll(exams) - } - - fun deleteExams(exams: List) { - examDb.deleteAll(exams) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt deleted file mode 100644 index f6d653a61..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.data.repositories.exam - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate -import io.reactivex.Single -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ExamRemote @Inject constructor(private val api: Api) { - - fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getExams(startDate, endDate) }.map { exams -> - exams.map { - Exam( - studentId = semester.studentId, - diaryId = semester.diaryId, - date = it.date.toLocalDate(), - entryDate = it.entryDate.toLocalDate(), - subject = it.subject, - group = it.group, - type = it.type, - description = it.description, - teacher = it.teacher, - teacherSymbol = it.teacherSymbol - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt deleted file mode 100644 index be60eaecd..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.wulkanowy.data.repositories.exam - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.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 deleted file mode 100644 index 4983a4740..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeLocal.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import io.github.wulkanowy.data.db.dao.GradeDao -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeLocal @Inject constructor(private val gradeDb: GradeDao) { - - fun saveGrades(grades: List) { - gradeDb.insertAll(grades) - } - - fun deleteGrades(grades: List) { - gradeDb.deleteAll(grades) - } - - fun updateGrades(grades: List) { - gradeDb.updateAll(grades) - } - - fun getGrades(semester: Semester): Maybe> { - return gradeDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt deleted file mode 100644 index 570ab7a77..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeRemote @Inject constructor(private val api: Api) { - - fun getGrades(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getGrades(semester.semesterId) } - .map { grades -> - grades.map { - Grade( - semesterId = semester.semesterId, - studentId = semester.studentId, - subject = it.subject, - entry = it.entry, - value = it.value, - modifier = it.modifier, - comment = it.comment, - color = it.color, - gradeSymbol = it.symbol.orEmpty(), - description = it.description.orEmpty(), - weight = it.weight, - weightValue = it.weightValue, - date = it.date.toLocalDate(), - teacher = it.teacher - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt deleted file mode 100644 index 7f8e92a2e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.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 deleted file mode 100644 index e74641d3a..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryLocal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradessummary - -import io.github.wulkanowy.data.db.dao.GradeSummaryDao -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeSummaryLocal @Inject constructor(private val gradeSummaryDb: GradeSummaryDao) { - - fun saveGradesSummary(gradesSummary: List) { - gradeSummaryDb.insertAll(gradesSummary) - } - - fun deleteGradesSummary(gradesSummary: List) { - gradeSummaryDb.deleteAll(gradesSummary) - } - - fun getGradesSummary(semester: Semester): Maybe> { - return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt deleted file mode 100644 index d395decf8..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradessummary - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeSummaryRemote @Inject constructor(private val api: Api) { - - fun getGradeSummary(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getGradesSummary(semester.semesterId) } - .map { gradesSummary -> - gradesSummary.map { - GradeSummary( - semesterId = semester.semesterId, - studentId = semester.studentId, - subject = it.name, - predictedGrade = it.predicted, - finalGrade = it.final - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt deleted file mode 100644 index 660a032c2..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradessummary - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.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 deleted file mode 100644 index 581ac2f81..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradestatistics - -import io.github.wulkanowy.data.db.dao.GradeStatisticsDao -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeStatisticsLocal @Inject constructor(private val gradeStatisticsDb: GradeStatisticsDao) { - - fun getGradesStatistics(semester: Semester, isSemester: Boolean): Maybe> { - return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester) - .filter { !it.isEmpty() } - } - - fun getGradesStatistics(semester: Semester, isSemester: Boolean, subjectName: String): Maybe> { - return (if ("Wszystkie" == subjectName) gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester).map { list -> - list.groupBy { it.grade }.map { - GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key, it.value.fold(0) { acc, e -> acc + e.amount }, false) - } - } - else gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)).filter { !it.isEmpty() } - } - - fun saveGradesStatistics(gradesStatistics: List) { - gradeStatisticsDb.insertAll(gradesStatistics) - } - - fun deleteGradesStatistics(gradesStatistics: List) { - gradeStatisticsDb.deleteAll(gradesStatistics) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt deleted file mode 100644 index fa3b951f6..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradestatistics - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeStatisticsRemote @Inject constructor(private val api: Api) { - - fun getGradeStatistics(semester: Semester, isSemester: Boolean): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getGradesStatistics(semester.semesterId, isSemester) } - .map { gradeStatistics -> - gradeStatistics.map { - GradeStatistics( - semesterId = semester.semesterId, - studentId = semester.studentId, - subject = it.subject, - grade = it.gradeValue, - amount = it.amount ?: 0, - semester = isSemester - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt deleted file mode 100644 index 9617cdd64..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradestatistics - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.Semester -import io.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 deleted file mode 100644 index 671ecafd7..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkLocal.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.repositories.homework - -import io.github.wulkanowy.data.db.dao.HomeworkDao -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) { - - fun saveHomework(homework: List) { - homeworkDb.insertAll(homework) - } - - fun deleteHomework(homework: List) { - homeworkDb.deleteAll(homework) - } - - fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { - return homeworkDb.loadAll(semester.semesterId, semester.studentId, startDate, endDate) - .filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt deleted file mode 100644 index 681b66469..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.repositories.homework - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate -import io.reactivex.Single -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class HomeworkRemote @Inject constructor(private val api: Api) { - - fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getHomework(startDate, endDate) } - .map { homework -> - homework.map { - Homework( - semesterId = semester.semesterId, - studentId = semester.studentId, - date = it.date.toLocalDate(), - entryDate = it.entryDate.toLocalDate(), - subject = it.subject, - content = it.content, - teacher = it.teacher, - teacherSymbol = it.teacherSymbol - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt deleted file mode 100644 index 3f924944d..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.wulkanowy.data.repositories.homework - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.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 deleted file mode 100644 index 115c89652..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.repositories.luckynumber - -import io.github.wulkanowy.data.db.dao.LuckyNumberDao -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumberDao) { - - fun saveLuckyNumber(luckyNumber: LuckyNumber) { - luckyNumberDb.insert(luckyNumber) - } - - fun updateLuckyNumber(luckyNumber: LuckyNumber) { - luckyNumberDb.update(luckyNumber) - } - - fun deleteLuckyNumber(luckyNumber: LuckyNumber) { - luckyNumberDb.delete(luckyNumber) - } - - fun getLuckyNumber(semester: Semester, date: LocalDate): Maybe { - return luckyNumberDb.load(semester.studentId, date) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt deleted file mode 100644 index 1b0f12b3e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.repositories.luckynumber - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Maybe -import io.reactivex.Single -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class LuckyNumberRemote @Inject constructor(private val api: Api) { - - fun getLuckyNumber(semester: Semester): Maybe { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMapMaybe { it.getLuckyNumber() } - .map { - LuckyNumber( - studentId = semester.studentId, - date = LocalDate.now(), - luckyNumber = it - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt deleted file mode 100644 index 4036521f7..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.wulkanowy.data.repositories.luckynumber - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Semester -import io.reactivex.Completable -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class LuckyNumberRepository @Inject constructor( - private val settings: InternetObservingSettings, - private val local: LuckyNumberLocal, - private val remote: LuckyNumberRemote -) { - - fun getLuckyNumber(semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Maybe { - return local.getLuckyNumber(semester, LocalDate.now()).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMapMaybe { - if (it) remote.getLuckyNumber(semester) - else Maybe.error(UnknownHostException()) - }.flatMap { new -> - local.getLuckyNumber(semester, LocalDate.now()) - .doOnSuccess { old -> - if (new != old) { - local.deleteLuckyNumber(old) - local.saveLuckyNumber(new.apply { - if (notify) isNotified = false - }) - } - } - .doOnComplete { - local.saveLuckyNumber(new.apply { - if (notify) isNotified = false - }) - } - }.flatMap({ local.getLuckyNumber(semester, LocalDate.now()) }, { Maybe.error(it) }, - { local.getLuckyNumber(semester, LocalDate.now()) }) - ) - } - - fun getNotNotifiedLuckyNumber(semester: Semester): Maybe { - return local.getLuckyNumber(semester, LocalDate.now()).filter { !it.isNotified } - } - - fun updateLuckyNumber(luckyNumber: LuckyNumber): Completable { - return Completable.fromCallable { local.updateLuckyNumber(luckyNumber) } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt deleted file mode 100644 index 06f5a1e05..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.wulkanowy.data.repositories.message - -enum class MessageFolder(val id: Int = 1) { - RECEIVED(1), - SENT(2), - TRASHED(3) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt deleted file mode 100644 index ce08d13ec..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.data.repositories.message - -import io.github.wulkanowy.data.db.dao.MessagesDao -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) { - - fun saveMessages(messages: List) { - messagesDb.insertAll(messages) - } - - fun updateMessages(messages: List) { - messagesDb.updateAll(messages) - } - - fun deleteMessages(messages: List) { - messagesDb.deleteAll(messages) - } - - fun getMessage(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 deleted file mode 100644 index b2bff1e77..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt +++ /dev/null @@ -1,66 +0,0 @@ -package io.github.wulkanowy.data.repositories.message - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.api.messages.Folder -import io.github.wulkanowy.api.messages.SentMessage -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.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 deleted file mode 100644 index c10cd5181..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt +++ /dev/null @@ -1,110 +0,0 @@ -package io.github.wulkanowy.data.repositories.message - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.api.messages.SentMessage -import io.github.wulkanowy.data.ApiHelper -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED -import io.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/note/NoteLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt deleted file mode 100644 index 784e61f0f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.repositories.note - -import io.github.wulkanowy.data.db.dao.NoteDao -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class NoteLocal @Inject constructor(private val noteDb: NoteDao) { - - fun saveNotes(notes: List) { - noteDb.insertAll(notes) - } - - fun updateNotes(notes: List) { - noteDb.updateAll(notes) - } - - fun deleteNotes(notes: List) { - noteDb.deleteAll(notes) - } - - fun getNotes(student: Student): Maybe> { - return noteDb.loadAll(student.studentId).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt deleted file mode 100644 index aebc6230e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.repositories.note - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class NoteRemote @Inject constructor(private val api: Api) { - - fun getNotes(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getNotes() } - .map { notes -> - notes.map { - Note( - studentId = semester.studentId, - date = it.date.toLocalDate(), - teacher = it.teacher, - category = it.category, - content = it.content - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt deleted file mode 100644 index 52cb2d0f5..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.wulkanowy.data.repositories.note - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.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 deleted file mode 100644 index 91717ed77..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.data.repositories.preferences - -import android.content.Context -import android.content.SharedPreferences -import io.github.wulkanowy.R -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class PreferencesRepository @Inject constructor( - private val sharedPref: SharedPreferences, - val context: Context -) { - val startMenuIndex: Int - get() = sharedPref.getString(context.getString(R.string.pref_key_start_menu), "0")?.toIntOrNull() ?: 0 - - val isShowPresent: Boolean - get() = sharedPref.getBoolean(context.getString(R.string.pref_key_attendance_present), true) - - val 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, true) - - val isNotificationsEnable: Boolean - get() = sharedPref.getBoolean(context.getString(R.string.pref_key_notifications_enable), true) - - val isDebugNotificationEnableKey: String = context.getString(R.string.pref_key_notification_debug) - val isDebugNotificationEnable: Boolean - get() = sharedPref.getBoolean(isDebugNotificationEnableKey, false) - - val gradePlusModifier: Double - get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0 - - val gradeMinusModifier: Double - get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble() ?: 0.0 - - val fillMessageContent: Boolean - get() = sharedPref.getBoolean(context.getString(R.string.pref_key_fill_message_content), true) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt deleted file mode 100644 index 6b8328ec2..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.recipient - -import io.github.wulkanowy.data.db.dao.RecipientDao -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class RecipientLocal @Inject constructor(private val recipientDb: RecipientDao) { - - fun getRecipients(student: Student, role: Int, unit: ReportingUnit): Maybe> { - return recipientDb.load(student.studentId, role, unit.realId).filter { !it.isEmpty() } - } - - fun saveRecipients(recipients: List) { - return recipientDb.insertAll(recipients) - } - - fun deleteRecipients(recipients: List) { - recipientDb.deleteAll(recipients) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt deleted file mode 100644 index b726edda9..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.data.repositories.recipient - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton -import io.github.wulkanowy.api.messages.Recipient as ApiRecipient - -@Singleton -class RecipientRemote @Inject constructor(private val api: Api) { - - fun getRecipients(role: Int, unit: ReportingUnit): Single> { - return api.getRecipients(unit.realId, role) - .map { recipients -> - recipients.map { it.toRecipient() } - } - } - - fun getMessageRecipients(message: Message): Single> { - return api.getMessageRecipients(message.messageId, message.senderId) - .map { recipients -> - recipients.map { it.toRecipient() } - } - } - - private fun ApiRecipient.toRecipient(): Recipient { - return Recipient( - studentId = api.studentId, - realId = id, - realName = name, - name = shortName, - hash = hash, - loginId = loginId, - role = role, - unitId = reportingUnitId ?: 0 - ) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt deleted file mode 100644 index cde75ea8b..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.data.repositories.recipient - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.ApiHelper -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.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 deleted file mode 100644 index b4281cbfd..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.repositories.reportingunit - -import io.github.wulkanowy.data.db.dao.ReportingUnitDao -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ReportingUnitLocal @Inject constructor(private val reportingUnitDb: ReportingUnitDao) { - - fun getReportingUnits(student: Student): Maybe> { - return reportingUnitDb.load(student.studentId).filter { !it.isEmpty() } - } - - fun getReportingUnit(student: Student, unitId: Int): Maybe { - return reportingUnitDb.loadOne(student.studentId, unitId) - } - - fun saveReportingUnits(reportingUnits: List) { - return reportingUnitDb.insertAll(reportingUnits) - } - - fun deleteReportingUnits(reportingUnits: List) { - reportingUnitDb.deleteAll(reportingUnits) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt deleted file mode 100644 index feb4b0134..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.repositories.reportingunit - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ReportingUnitRemote @Inject constructor(private val api: Api) { - - fun getReportingUnits(): Single> { - return api.getReportingUnits().map { - it.map { unit -> - ReportingUnit( - studentId = api.studentId, - realId = unit.id, - roles = unit.roles, - senderId = unit.senderId, - senderName = unit.senderName, - shortName = unit.short - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt deleted file mode 100644 index 6758898e2..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt +++ /dev/null @@ -1,56 +0,0 @@ -package io.github.wulkanowy.data.repositories.reportingunit - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.ApiHelper -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.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 deleted file mode 100644 index b9750e7d5..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.repositories.semester - -import io.github.wulkanowy.data.db.dao.SemesterDao -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) { - - fun saveSemesters(semesters: List) { - semesterDb.insertAll(semesters) - } - - fun deleteSemesters(semesters: List) { - semesterDb.deleteAll(semesters) - } - - fun getSemesters(student: Student): Maybe> { - return semesterDb.loadAll(student.studentId, student.classId).filter { !it.isEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt deleted file mode 100644 index c199c16c0..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.data.repositories.semester - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SemesterRemote @Inject constructor(private val api: Api) { - - fun getSemesters(student: Student): Single> { - return api.getSemesters().map { semesters -> - semesters.map { semester -> - Semester( - studentId = student.studentId, - diaryId = semester.diaryId, - diaryName = semester.diaryName, - 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 deleted file mode 100644 index 593014032..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.data.repositories.semester - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.ApiHelper -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.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 deleted file mode 100644 index e6d744213..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.wulkanowy.data.repositories.student - -import android.content.Context -import io.github.wulkanowy.data.db.dao.StudentDao -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.security.decrypt -import io.github.wulkanowy.utils.security.encrypt -import io.reactivex.Completable -import io.reactivex.Maybe -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class StudentLocal @Inject constructor( - private val studentDb: StudentDao, - private val context: Context -) { - - fun 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 deleted file mode 100644 index 251d38344..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.data.repositories.student - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Single -import org.threeten.bp.LocalDateTime.now -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class StudentRemote @Inject constructor(private val api: Api) { - - fun getStudents(email: String, password: String, endpoint: String): Single> { - return api.getStudents().map { students -> - students.map { student -> - Student( - email = email, - password = password, - symbol = student.symbol, - studentId = student.studentId, - studentName = student.studentName, - schoolSymbol = student.schoolSymbol, - schoolName = student.schoolName, - 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 deleted file mode 100644 index 5c4a60558..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.wulkanowy.data.repositories.student - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.ApiHelper -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.exceptions.NoCurrentStudentException -import io.reactivex.Completable -import io.reactivex.Maybe -import io.reactivex.Single -import java.net.UnknownHostException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class StudentRepository @Inject constructor( - private val local: StudentLocal, - private val remote: StudentRemote, - private val settings: InternetObservingSettings, - private val apiHelper: ApiHelper -) { - - fun isStudentSaved(): Single = local.getStudents(false).isEmpty.map { !it } - - fun 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 deleted file mode 100644 index 63e334019..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.subject - -import io.github.wulkanowy.data.db.dao.SubjectDao -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Subject -import io.reactivex.Maybe -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SubjectLocal @Inject constructor(private val subjectDao: SubjectDao) { - - fun getSubjects(semester: Semester): Maybe> { - return subjectDao.loadAll(semester.diaryId, semester.studentId) - .filter { !it.isEmpty() } - } - - fun saveSubjects(subjects: List) { - subjectDao.insertAll(subjects) - } - - fun deleteSubjects(subjects: List) { - subjectDao.deleteAll(subjects) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt deleted file mode 100644 index 88fbb196b..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.data.repositories.subject - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Subject -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SubjectRemote @Inject constructor(private val api: Api) { - - fun getSubjects(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { api.getSubjects() } - .map { subjects -> - subjects.map { - Subject( - studentId = semester.studentId, - diaryId = semester.diaryId, - name = it.name, - realId = it.value - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt deleted file mode 100644 index 0c5f386b6..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.data.repositories.subject - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Subject -import io.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 deleted file mode 100644 index e074ce2a1..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import io.github.wulkanowy.data.db.dao.TimetableDao -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Timetable -import io.reactivex.Maybe -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao) { - - fun saveTimetable(timetables: List) { - timetableDb.insertAll(timetables) - } - - fun deleteTimetable(timetables: List) { - timetableDb.deleteAll(timetables) - } - - fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { - return timetableDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate).filter { it.isNotEmpty() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt deleted file mode 100644 index 77742e7b3..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.utils.toLocalDate -import io.github.wulkanowy.utils.toLocalDateTime -import io.reactivex.Single -import org.threeten.bp.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class TimetableRemote @Inject constructor(private val api: Api) { - - fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getTimetable(startDate, endDate) } - .map { lessons -> - lessons.map { - Timetable( - studentId = semester.studentId, - diaryId = semester.diaryId, - number = it.number, - start = it.start.toLocalDateTime(), - end = it.end.toLocalDateTime(), - date = it.date.toLocalDate(), - subject = it.subject, - subjectOld = it.subjectOld, - group = it.group, - room = it.room, - roomOld = it.roomOld, - teacher = it.teacher, - teacherOld = it.teacherOld, - info = it.info, - changes = it.changes, - canceled = it.canceled - ) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt deleted file mode 100644 index 0e6331f91..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt +++ /dev/null @@ -1,49 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.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 new file mode 100644 index 000000000..45f8c1f59 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/AccountSync.java @@ -0,0 +1,197 @@ +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 new file mode 100644 index 000000000..187694189 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/AttendanceSync.java @@ -0,0 +1,140 @@ +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 new file mode 100644 index 000000000..db4730921 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/ExamsSync.java @@ -0,0 +1,135 @@ +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 new file mode 100644 index 000000000..59a71845c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/GradeSync.java @@ -0,0 +1,81 @@ +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 new file mode 100644 index 000000000..ddba1a216 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/NotRegisteredUserException.java @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..1d0300d69 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/SubjectSync.java @@ -0,0 +1,66 @@ +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 new file mode 100644 index 000000000..67797ad1e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/SyncContract.java @@ -0,0 +1,40 @@ +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 new file mode 100644 index 000000000..05dd14fdb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/SyncRepository.java @@ -0,0 +1,123 @@ +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 new file mode 100644 index 000000000..1ea2fa488 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/sync/TimetableSync.java @@ -0,0 +1,161 @@ +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 new file mode 100644 index 000000000..92f5d9a19 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/AppComponent.java @@ -0,0 +1,20 @@ +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 deleted file mode 100644 index 3ac3a525b..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/AppComponent.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.wulkanowy.di - -import dagger.Component -import dagger.android.AndroidInjector -import dagger.android.support.AndroidSupportInjectionModule -import io.github.wulkanowy.WulkanowyApp -import io.github.wulkanowy.data.RepositoryModule -import io.github.wulkanowy.services.ServicesModule -import javax.inject.Singleton - -@Singleton -@Component(modules = [ - AndroidSupportInjectionModule::class, - AppModule::class, - RepositoryModule::class, - ServicesModule::class, - BuilderModule::class]) -interface AppComponent : AndroidInjector { - - @Component.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 new file mode 100644 index 000000000..d6d4dfa2f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/AppModule.java @@ -0,0 +1,78 @@ +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 deleted file mode 100644 index da1d8f7a8..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.wulkanowy.di - -import android.appwidget.AppWidgetManager -import android.content.Context -import com.google.firebase.analytics.FirebaseAnalytics -import dagger.Module -import dagger.Provides -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.BuildConfig.DEBUG -import io.github.wulkanowy.WulkanowyApp -import io.github.wulkanowy.utils.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 provideFirebaseAnalytics(context: Context) = FirebaseAnalytics.getInstance(context) - - @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 new file mode 100644 index 000000000..74b77d520 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.java @@ -0,0 +1,39 @@ +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 deleted file mode 100644 index a963e9a96..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.wulkanowy.di - -import dagger.Module -import dagger.android.ContributesAndroidInjector -import io.github.wulkanowy.di.scopes.PerActivity -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.login.LoginModule -import io.github.wulkanowy.ui.modules.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 new file mode 100644 index 000000000..c9fc8a5b2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/scopes/PerActivity.java @@ -0,0 +1,11 @@ +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 deleted file mode 100644 index c1b4352a4..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/scopes/PerActivity.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.wulkanowy.di.scopes - -import javax.inject.Scope - -@Scope -@Retention(AnnotationRetention.RUNTIME) -annotation class PerActivity diff --git a/app/src/main/java/io/github/wulkanowy/di/scopes/PerChildFragment.java b/app/src/main/java/io/github/wulkanowy/di/scopes/PerChildFragment.java new file mode 100644 index 000000000..67a9f8209 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/scopes/PerChildFragment.java @@ -0,0 +1,11 @@ +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 deleted file mode 100644 index 08884dac5..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/scopes/PerChildFragment.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.wulkanowy.di.scopes - -import javax.inject.Scope - -@Scope -@Retention(AnnotationRetention.RUNTIME) -annotation class PerChildFragment diff --git a/app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.java b/app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.java new file mode 100644 index 000000000..a4d37c382 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.java @@ -0,0 +1,11 @@ +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 deleted file mode 100644 index 1203d7d88..000000000 --- a/app/src/main/java/io/github/wulkanowy/di/scopes/PerFragment.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.wulkanowy.di.scopes - -import javax.inject.Scope - -@Scope -@Retention(AnnotationRetention.RUNTIME) -annotation class PerFragment diff --git a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt deleted file mode 100644 index 84409a845..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt +++ /dev/null @@ -1,107 +0,0 @@ -package io.github.wulkanowy.services - -import android.app.NotificationManager -import android.content.Context -import android.content.Context.NOTIFICATION_SERVICE -import androidx.core.app.NotificationManagerCompat -import androidx.work.WorkManager -import com.squareup.inject.assisted.dagger2.AssistedModule -import dagger.Binds -import dagger.Module -import dagger.Provides -import dagger.android.ContributesAndroidInjector -import dagger.multibindings.IntoSet -import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork -import io.github.wulkanowy.services.sync.works.AttendanceWork -import io.github.wulkanowy.services.sync.works.CompletedLessonWork -import io.github.wulkanowy.services.sync.works.ExamWork -import io.github.wulkanowy.services.sync.works.GradeStatisticsWork -import io.github.wulkanowy.services.sync.works.GradeSummaryWork -import io.github.wulkanowy.services.sync.works.GradeWork -import io.github.wulkanowy.services.sync.works.HomeworkWork -import io.github.wulkanowy.services.sync.works.LuckyNumberWork -import io.github.wulkanowy.services.sync.works.MessageWork -import io.github.wulkanowy.services.sync.works.NoteWork -import io.github.wulkanowy.services.sync.works.RecipientWork -import io.github.wulkanowy.services.sync.works.TimetableWork -import io.github.wulkanowy.services.sync.works.Work -import io.github.wulkanowy.services.widgets.TimetableWidgetService -import javax.inject.Singleton - -@AssistedModule -@Module(includes = [AssistedInject_ServicesModule::class]) -abstract class ServicesModule { - - @Module - companion object { - - @JvmStatic - @Provides - fun provideWorkManager() = WorkManager.getInstance() - - @JvmStatic - @Singleton - @Provides - fun provideNotificationManagerCompat(context: Context) = NotificationManagerCompat.from(context) - - @JvmStatic - @Singleton - @Provides - fun provideNotificationManager(context: Context) = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager - } - - @ContributesAndroidInjector - abstract fun bindTimetableWidgetService(): TimetableWidgetService - - @Binds - @IntoSet - abstract fun provideGradeWork(work: GradeWork): Work - - @Binds - @IntoSet - abstract fun provideNoteWork(work: NoteWork): Work - - @Binds - @IntoSet - abstract fun provideAttendanceWork(work: AttendanceWork): Work - - @Binds - @IntoSet - abstract fun provideGradeSummaryWork(work: GradeSummaryWork): Work - - @Binds - @IntoSet - abstract fun provideExamWork(work: ExamWork): Work - - @Binds - @IntoSet - abstract fun provideAttendanceSummaryWork(work: AttendanceSummaryWork): Work - - @Binds - @IntoSet - abstract fun provideTimetableWork(work: TimetableWork): Work - - @Binds - @IntoSet - abstract fun provideLuckyNumberWork(work: LuckyNumberWork): Work - - @Binds - @IntoSet - abstract fun provideCompletedLessonWork(work: CompletedLessonWork): Work - - @Binds - @IntoSet - abstract fun provideHomeworkWork(work: HomeworkWork): Work - - @Binds - @IntoSet - abstract fun provideMessageWork(work: MessageWork): Work - - @Binds - @IntoSet - abstract fun provideRecipientWork(work: RecipientWork): Work - - @Binds - @IntoSet - abstract fun provideGradeStatistics(work: GradeStatisticsWork): Work -} diff --git a/app/src/main/java/io/github/wulkanowy/services/jobs/SyncJob.java b/app/src/main/java/io/github/wulkanowy/services/jobs/SyncJob.java new file mode 100644 index 000000000..9854666ef --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/jobs/SyncJob.java @@ -0,0 +1,148 @@ +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 new file mode 100644 index 000000000..a554bea39 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/notifies/GradeNotify.java @@ -0,0 +1,36 @@ +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 new file mode 100644 index 000000000..156de2bd7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/notifies/NotificationService.java @@ -0,0 +1,57 @@ +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 deleted file mode 100644 index c1820afb9..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.services.sync - -import android.os.Build.VERSION.SDK_INT -import android.os.Build.VERSION_CODES.O -import androidx.work.BackoffPolicy.EXPONENTIAL -import androidx.work.Constraints -import androidx.work.ExistingPeriodicWorkPolicy.KEEP -import androidx.work.ExistingPeriodicWorkPolicy.REPLACE -import androidx.work.NetworkType.METERED -import androidx.work.NetworkType.UNMETERED -import androidx.work.PeriodicWorkRequest -import androidx.work.WorkManager -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.services.sync.channels.DebugChannel -import io.github.wulkanowy.services.sync.channels.NewEntriesChannel -import io.github.wulkanowy.utils.isHolidays -import org.threeten.bp.LocalDate.now -import timber.log.Timber -import java.util.concurrent.TimeUnit.MINUTES -import javax.inject.Inject -import javax.inject.Named -import javax.inject.Singleton - -@Singleton -class SyncManager @Inject constructor( - private val workManager: WorkManager, - private val preferencesRepository: PreferencesRepository, - newEntriesChannel: NewEntriesChannel, - debugChannel: DebugChannel, - @Named("isDebug") isDebug: Boolean -) { - - init { - if (SDK_INT >= O) newEntriesChannel.create() - if (SDK_INT >= O && isDebug) debugChannel.create() - if (now().isHolidays) stopSyncWorker() - Timber.i("SyncManager was initialized") - } - - fun startSyncWorker(restart: Boolean = false) { - if (preferencesRepository.isServiceEnabled && !now().isHolidays) { - workManager.enqueueUniquePeriodicWork(SyncWorker::class.java.simpleName, if (restart) REPLACE else KEEP, - PeriodicWorkRequest.Builder(SyncWorker::class.java, preferencesRepository.servicesInterval, MINUTES, 10, MINUTES) - .setBackoffCriteria(EXPONENTIAL, 30, MINUTES) - .setConstraints(Constraints.Builder() - .setRequiredNetworkType(if (preferencesRepository.isServicesOnlyWifi) METERED else UNMETERED) - .build()) - .build()) - } - } - - fun stopSyncWorker() { - workManager.cancelUniqueWork(SyncWorker::class.java.simpleName) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt deleted file mode 100644 index 6a4c4ca6d..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt +++ /dev/null @@ -1,81 +0,0 @@ -package io.github.wulkanowy.services.sync - -import android.content.Context -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationCompat.BigTextStyle -import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT -import androidx.core.app.NotificationManagerCompat -import androidx.work.ListenableWorker -import androidx.work.RxWorker -import androidx.work.WorkerParameters -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject -import io.github.wulkanowy.R -import io.github.wulkanowy.api.interceptor.FeatureDisabledException -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.services.sync.channels.DebugChannel -import io.github.wulkanowy.services.sync.works.Work -import io.github.wulkanowy.utils.getCompatColor -import io.reactivex.Completable -import io.reactivex.Single -import timber.log.Timber -import kotlin.random.Random - -class SyncWorker @AssistedInject constructor( - @Assisted appContext: Context, - @Assisted workerParameters: WorkerParameters, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val works: Set<@JvmSuppressWildcards Work>, - private val preferencesRepository: PreferencesRepository, - private val notificationManager: NotificationManagerCompat -) : RxWorker(appContext, workerParameters) { - - override fun createWork(): Single { - Timber.i("SyncWorker is starting") - return studentRepository.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 deleted file mode 100644 index aadfc27f4..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorkerFactory.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.services.sync - -import android.content.Context -import androidx.work.ListenableWorker -import androidx.work.WorkerFactory -import androidx.work.WorkerParameters -import timber.log.Timber -import javax.inject.Inject - -class SyncWorkerFactory @Inject constructor(private val syncWorkerFactory: SyncWorker.Factory) : WorkerFactory() { - - override fun createWorker(appContext: Context, workerClassName: String, workerParameters: WorkerParameters): ListenableWorker? { - return if (workerClassName == SyncWorker::class.java.name) { - syncWorkerFactory.create(appContext, workerParameters) - } else { - Timber.e(IllegalArgumentException("Unknown worker class name: $workerClassName")) - null - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt deleted file mode 100644 index 02fff5690..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/channels/DebugChannel.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.services.sync.channels - -import android.annotation.TargetApi -import android.app.Notification.VISIBILITY_PUBLIC -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.NotificationManager.IMPORTANCE_DEFAULT -import android.content.Context -import io.github.wulkanowy.R -import javax.inject.Inject - -@TargetApi(26) -class DebugChannel @Inject constructor( - private val notificationManager: NotificationManager, - private val context: Context -) { - - companion object { - const val CHANNEL_ID = "debug_channel" - } - - fun create() { - notificationManager.createNotificationChannel( - NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_debug), IMPORTANCE_DEFAULT).apply { - lockscreenVisibility = VISIBILITY_PUBLIC - }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewEntriesChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewEntriesChannel.kt deleted file mode 100644 index 8e24a2a67..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewEntriesChannel.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.services.sync.channels - -import android.annotation.TargetApi -import android.app.Notification.VISIBILITY_PUBLIC -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.NotificationManager.IMPORTANCE_HIGH -import android.content.Context -import io.github.wulkanowy.R -import javax.inject.Inject - -@TargetApi(26) -class NewEntriesChannel @Inject constructor( - private val notificationManager: NotificationManager, - private val context: Context -) { - - companion object { - const val CHANNEL_ID = "new_entries_channel" - } - - fun create() { - notificationManager.createNotificationChannel( - NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_entries), IMPORTANCE_HIGH).apply { - enableLights(true) - enableVibration(true) - lockscreenVisibility = VISIBILITY_PUBLIC - }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt deleted file mode 100644 index 01978c5b6..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.attendancesummary.AttendanceSummaryRepository -import io.reactivex.Completable -import javax.inject.Inject - -class AttendanceSummaryWork @Inject constructor( - private val attendanceSummaryRepository: AttendanceSummaryRepository -) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return attendanceSummaryRepository.getAttendanceSummary(semester, -1, true).ignoreElement() - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt deleted file mode 100644 index e4b55b0ea..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Completable -import org.threeten.bp.LocalDate.now -import javax.inject.Inject - -class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return attendanceRepository.getAttendance(semester, now().monday, now().friday, true) - .ignoreElement() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt deleted file mode 100644 index 29642ad6f..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Completable -import org.threeten.bp.LocalDate.now -import javax.inject.Inject - -class CompletedLessonWork @Inject constructor( - private val completedLessonsRepository: CompletedLessonsRepository -) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return completedLessonsRepository.getCompletedLessons(semester, now().monday, now().friday, true) - .ignoreElement() - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt deleted file mode 100644 index 8744fcc79..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.exam.ExamRepository -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Completable -import org.threeten.bp.LocalDate.now -import javax.inject.Inject - -class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return examRepository.getExams(semester, now().monday, now().friday, true).ignoreElement() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt deleted file mode 100644 index 1de39a95f..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.gradestatistics.GradeStatisticsRepository -import io.reactivex.Completable -import javax.inject.Inject - -class GradeStatisticsWork @Inject constructor(private val gradeStatisticsRepository: GradeStatisticsRepository) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return gradeStatisticsRepository.getGradesStatistics(semester, "Wszystkie", false, true) - .ignoreElement() - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt deleted file mode 100644 index 6de0bc5b0..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository -import io.reactivex.Completable -import javax.inject.Inject - -class GradeSummaryWork @Inject constructor(private val gradeSummaryRepository: GradeSummaryRepository) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return gradeSummaryRepository.getGradesSummary(semester, true).ignoreElement() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt deleted file mode 100644 index 0c70b476f..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt +++ /dev/null @@ -1,61 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.content.Context -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationCompat.DEFAULT_ALL -import androidx.core.app.NotificationCompat.PRIORITY_HIGH -import androidx.core.app.NotificationManagerCompat -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.grade.GradeRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.services.sync.channels.NewEntriesChannel -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.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 deleted file mode 100644 index 32b356c68..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.homework.HomeworkRepository -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Completable -import org.threeten.bp.LocalDate.now -import javax.inject.Inject - -class HomeworkWork @Inject constructor(private val homeworkRepository: HomeworkRepository) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return homeworkRepository.getHomework(semester, now().monday, now().friday, true).ignoreElement() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt deleted file mode 100644 index ebd536b75..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.content.Context -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationCompat.DEFAULT_ALL -import androidx.core.app.NotificationCompat.PRIORITY_HIGH -import androidx.core.app.NotificationManagerCompat -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.services.sync.channels.NewEntriesChannel -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.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 deleted file mode 100644 index d5016aeed..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt +++ /dev/null @@ -1,61 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.content.Context -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationCompat.DEFAULT_ALL -import androidx.core.app.NotificationCompat.PRIORITY_HIGH -import androidx.core.app.NotificationManagerCompat -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED -import io.github.wulkanowy.data.repositories.message.MessageRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.services.sync.channels.NewEntriesChannel -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.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 deleted file mode 100644 index 86e540c43..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.content.Context -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationCompat.DEFAULT_ALL -import androidx.core.app.NotificationCompat.PRIORITY_HIGH -import androidx.core.app.NotificationManagerCompat -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.note.NoteRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.services.sync.channels.NewEntriesChannel -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.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 deleted file mode 100644 index fa610dee4..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.recipient.RecipientRepository -import io.github.wulkanowy.data.repositories.reportingunit.ReportingUnitRepository -import io.reactivex.Completable -import javax.inject.Inject - -class RecipientWork @Inject constructor( - private val reportingUnitRepository: ReportingUnitRepository, - private val recipientRepository: RecipientRepository -) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return reportingUnitRepository.getReportingUnits(student, true) - .flatMapCompletable { units -> - Completable.mergeDelayError(units.map { - recipientRepository.getRecipients(student, 2, it, true).ignoreElement() - }) - } - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt deleted file mode 100644 index 743ae0e83..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.timetable.TimetableRepository -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.monday -import io.reactivex.Completable -import org.threeten.bp.LocalDate.now -import javax.inject.Inject - -class TimetableWork @Inject constructor(private val timetableRepository: TimetableRepository) : Work { - - override fun create(student: Student, semester: Semester): Completable { - return timetableRepository.getTimetable(semester, now().monday, now().friday, true) - .ignoreElement() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt deleted file mode 100644 index 1601a103b..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.services.sync.works - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Completable - -interface Work { - - fun create(student: Student, semester: Semester): Completable -} - diff --git a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt deleted file mode 100644 index f3429457b..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.services.widgets - -import android.content.Intent -import android.widget.RemoteViewsService -import dagger.android.AndroidInjection -import io.github.wulkanowy.data.db.SharedPrefHelper -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.timetable.TimetableRepository -import io.github.wulkanowy.ui.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 new file mode 100644 index 000000000..f442542ef --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetServices.java @@ -0,0 +1,22 @@ +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 new file mode 100644 index 000000000..82ab9d19f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.java @@ -0,0 +1,59 @@ +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 deleted file mode 100644 index d8b53607b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.ui.base - -import android.os.Bundle -import android.view.View -import android.widget.Toast -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.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 - - 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 onDestroy() { - super.onDestroy() - invalidateOptionsMenu() - } - - 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 new file mode 100644 index 000000000..3bfa40d13 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseContract.java @@ -0,0 +1,22 @@ +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 new file mode 100644 index 000000000..e2a5a9e02 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.java @@ -0,0 +1,56 @@ +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 deleted file mode 100644 index 4beb0ac4f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt +++ /dev/null @@ -1,32 +0,0 @@ -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) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt deleted file mode 100644 index fcf7e9809..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.ui.base - -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentPagerAdapter - -class BaseFragmentPagerAdapter(private val fragmentManager: FragmentManager) : FragmentPagerAdapter(fragmentManager) { - - private val pages = mutableMapOf() - - var containerId = 0 - - fun getFragmentInstance(position: Int): Fragment? { - if (containerId == 0) throw IllegalArgumentException("Container id is 0") - return fragmentManager.findFragmentByTag("android:switcher:$containerId:$position") - } - - fun addFragments(fragments: List) { - fragments.forEach { pages[it] = null } - } - - fun addFragmentsWithTitle(pages: Map) { - this.pages.putAll(pages) - } - - override fun getItem(position: Int) = pages.keys.elementAt(position) - - override fun getCount() = pages.size - - override fun getPageTitle(position: Int) = pages.values.elementAt(position) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BasePagerAdapter.java b/app/src/main/java/io/github/wulkanowy/ui/base/BasePagerAdapter.java new file mode 100644 index 000000000..178f01107 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BasePagerAdapter.java @@ -0,0 +1,47 @@ +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 new file mode 100644 index 000000000..8f9b8af0b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.java @@ -0,0 +1,41 @@ +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 deleted file mode 100644 index c5a31958d..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.ui.base - -import io.reactivex.disposables.CompositeDisposable - -open class BasePresenter(private val errorHandler: ErrorHandler) { - - val disposable = CompositeDisposable() - - var view: T? = null - - open fun onAttachView(view: T) { - this.view = view - errorHandler.showErrorMessage = { text, error -> view.showError(text, error) } - } - - open fun onDetachView() { - view = null - disposable.clear() - errorHandler.clear() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt deleted file mode 100644 index d2470fd17..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.wulkanowy.ui.base - -interface BaseView { - - fun showError(text: String, error: Throwable) - - fun showMessage(text: String) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt deleted file mode 100644 index db3918b90..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.ui.base - -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context.CLIPBOARD_SERVICE -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import android.widget.Toast.LENGTH_LONG -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import kotlinx.android.synthetic.main.dialog_error.* -import java.io.PrintWriter -import java.io.StringWriter - -class ErrorDialog : DialogFragment() { - - private lateinit var error: Throwable - - companion object { - private const val ARGUMENT_KEY = "Data" - - fun newInstance(error: Throwable): ErrorDialog { - return ErrorDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, error) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - error = getSerializable(ARGUMENT_KEY) as Throwable - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_error, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - StringWriter().let { writer -> - error.printStackTrace(PrintWriter(writer)) - - errorDialogContent.text = writer.toString() - errorDialogCopy.setOnClickListener { - ClipData.newPlainText("wulkanowyError", writer.toString()).let { clip -> - (activity?.getSystemService(CLIPBOARD_SERVICE) as? ClipboardManager)?.primaryClip = clip - } - Toast.makeText(context, R.string.all_copied, LENGTH_LONG).show() - } - } - errorDialogCancel.setOnClickListener { dismiss() } - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt deleted file mode 100644 index 83cbf0ef0..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.ui.base - -import android.content.res.Resources -import com.readystatesoftware.chuck.api.ChuckCollector -import io.github.wulkanowy.R -import io.github.wulkanowy.api.interceptor.FeatureDisabledException -import io.github.wulkanowy.api.interceptor.ServiceUnavailableException -import io.github.wulkanowy.api.login.NotLoggedInException -import timber.log.Timber -import java.net.SocketTimeoutException -import java.net.UnknownHostException -import javax.inject.Inject - -open class ErrorHandler @Inject constructor(protected val resources: Resources, private val chuckCollector: ChuckCollector) { - - var showErrorMessage: (String, Throwable) -> Unit = { _, _ -> } - - fun dispatch(error: Throwable) { - chuckCollector.onError(error.javaClass.simpleName, error) - Timber.e(error, "An exception occurred while the Wulkanowy was running") - proceed(error) - } - - protected open fun proceed(error: Throwable) { - showErrorMessage((when (error) { - is UnknownHostException -> resources.getString(R.string.error_no_internet) - is SocketTimeoutException -> resources.getString(R.string.error_timeout) - is NotLoggedInException -> resources.getString(R.string.error_login_failed) - is ServiceUnavailableException -> resources.getString(R.string.error_service_unavailable) - is FeatureDisabledException -> resources.getString(R.string.error_feature_disabled) - else -> resources.getString(R.string.error_unknown) - }), error) - } - - open fun clear() { - showErrorMessage = { _, _ -> } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt deleted file mode 100644 index 449f451be..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt +++ /dev/null @@ -1,33 +0,0 @@ -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/base/session/BaseSessionFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt deleted file mode 100644 index 110729d4f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.ui.base.session - -import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK -import android.content.Intent.FLAG_ACTIVITY_NEW_TASK -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.main.MainActivity - -open class BaseSessionFragment : BaseFragment(), BaseSessionView { - - override fun showExpiredDialog() { - (activity as? MainActivity)?.showExpiredDialog() - } - - override fun openLoginView() { - activity?.also { - startActivity(LoginActivity.getStartIntent(it) - .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) }) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt deleted file mode 100644 index 2c9f50ea9..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt +++ /dev/null @@ -1,15 +0,0 @@ -package io.github.wulkanowy.ui.base.session - -import io.github.wulkanowy.ui.base.BasePresenter - -open class BaseSessionPresenter(private val errorHandler: SessionErrorHandler) : - BasePresenter(errorHandler) { - - override fun onAttachView(view: T) { - super.onAttachView(view) - errorHandler.apply { - onDecryptionFail = { view.showExpiredDialog() } - onNoCurrentStudent = { view.openLoginView() } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt deleted file mode 100644 index 619c2863a..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.ui.base.session - -import io.github.wulkanowy.ui.base.BaseView - -interface BaseSessionView : BaseView { - - fun showExpiredDialog() - - fun openLoginView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt deleted file mode 100644 index 017eabbab..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.ui.base.session - -import android.content.res.Resources -import com.readystatesoftware.chuck.api.ChuckCollector -import io.github.wulkanowy.data.exceptions.NoCurrentStudentException -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.security.ScramblerException -import javax.inject.Inject - -open class SessionErrorHandler @Inject constructor( - resources: Resources, - chuckCollector: ChuckCollector -) : ErrorHandler(resources, chuckCollector) { - - var onDecryptionFail: () -> Unit = {} - - var onNoCurrentStudent: () -> Unit = {} - - override fun proceed(error: Throwable) { - when (error) { - is ScramblerException -> onDecryptionFail() - is NoCurrentStudentException -> onNoCurrentStudent() - else -> super.proceed(error) - } - } - - override fun clear() { - super.clear() - onDecryptionFail = {} - onNoCurrentStudent = {} - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java b/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java new file mode 100644 index 000000000..a0960af2c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginActivity.java @@ -0,0 +1,236 @@ +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 new file mode 100644 index 000000000..96bb49d6b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginContract.java @@ -0,0 +1,54 @@ +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 new file mode 100644 index 000000000..0cb26c496 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginModule.java @@ -0,0 +1,13 @@ +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 new file mode 100644 index 000000000..d8461bf3b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginPresenter.java @@ -0,0 +1,178 @@ +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 new file mode 100644 index 000000000..2938a836d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/login/LoginTask.java @@ -0,0 +1,60 @@ +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 new file mode 100644 index 000000000..b739ae98c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainActivity.java @@ -0,0 +1,170 @@ +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 new file mode 100644 index 000000000..cb4e29e46 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainContract.java @@ -0,0 +1,34 @@ +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 new file mode 100644 index 000000000..ff3fed67a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainModule.java @@ -0,0 +1,50 @@ +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 new file mode 100644 index 000000000..2ca9eefa9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/MainPresenter.java @@ -0,0 +1,63 @@ +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 new file mode 100644 index 000000000..21e3ba12f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/OnFragmentIsReadyListener.java @@ -0,0 +1,6 @@ +package io.github.wulkanowy.ui.main; + +public interface OnFragmentIsReadyListener { + + void onFragmentIsReady(); +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java new file mode 100644 index 000000000..1ad66787e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceContract.java @@ -0,0 +1,33 @@ +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 new file mode 100644 index 000000000..ea38e51d6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialogFragment.java @@ -0,0 +1,91 @@ +package io.github.wulkanowy.ui.main.attendance; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import io.github.wulkanowy.R; +import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson; + +public class AttendanceDialogFragment extends DialogFragment { + + private static final String ARGUMENT_KEY = "Item"; + + private AttendanceLesson lesson; + + @BindView(R.id.attendance_dialog_subject_value) + TextView subject; + + @BindView(R.id.attendance_dialog_date_value) + TextView date; + + @BindView(R.id.attendance_dialog_number_value) + TextView number; + + @BindView(R.id.attendance_dialog_description_value) + TextView description; + + public AttendanceDialogFragment() { + //empty constructor for fragment + } + + public static AttendanceDialogFragment newInstance(AttendanceLesson lesson) { + AttendanceDialogFragment dialogFragment = new AttendanceDialogFragment(); + + Bundle bundle = new Bundle(); + bundle.putSerializable(ARGUMENT_KEY, lesson); + + dialogFragment.setArguments(bundle); + + return dialogFragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + lesson = (AttendanceLesson) getArguments().getSerializable(ARGUMENT_KEY); + } + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.attendance_dialog, container, false); + + ButterKnife.bind(this, view); + + if (!lesson.getSubject().isEmpty()) { + subject.setText(lesson.getSubject()); + } + + if (!lesson.getDate().isEmpty()) { + date.setText(lesson.getDate()); + } + + if (0 != lesson.getNumber()) { + number.setText(String.valueOf(lesson.getNumber())); + } + + description.setText(lesson.getDescription()); + + if (lesson.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 new file mode 100644 index 000000000..732806db9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.java @@ -0,0 +1,102 @@ +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 new file mode 100644 index 000000000..8679cf100 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceModule.java @@ -0,0 +1,32 @@ +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 new file mode 100644 index 000000000..73a2837ca --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.java @@ -0,0 +1,108 @@ +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 new file mode 100644 index 000000000..86128109b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceHeader.java @@ -0,0 +1,151 @@ +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 new file mode 100644 index 000000000..65c8329ef --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceSubItem.java @@ -0,0 +1,122 @@ +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 new file mode 100644 index 000000000..a83e67e86 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabContract.java @@ -0,0 +1,30 @@ +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 new file mode 100644 index 000000000..8de474690 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabFragment.java @@ -0,0 +1,129 @@ +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 new file mode 100644 index 000000000..380a221f2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabModule.java @@ -0,0 +1,21 @@ +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 new file mode 100644 index 000000000..7d0c26e5a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/tab/AttendanceTabPresenter.java @@ -0,0 +1,194 @@ +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 new file mode 100644 index 000000000..aeeecea8b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsContract.java @@ -0,0 +1,33 @@ +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 new file mode 100644 index 000000000..8c4fafcd1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsDialogFragment.java @@ -0,0 +1,81 @@ +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 new file mode 100644 index 000000000..05557b81e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsFragment.java @@ -0,0 +1,100 @@ +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 new file mode 100644 index 000000000..8d56cf234 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsModule.java @@ -0,0 +1,32 @@ +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 new file mode 100644 index 000000000..20c0fe2e1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/ExamsPresenter.java @@ -0,0 +1,107 @@ +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 new file mode 100644 index 000000000..6c0c364ec --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsHeader.java @@ -0,0 +1,82 @@ +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 new file mode 100644 index 000000000..9ff10ebc1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsSubItem.java @@ -0,0 +1,109 @@ +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 new file mode 100644 index 000000000..4e0785bb2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabContract.java @@ -0,0 +1,30 @@ +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 new file mode 100644 index 000000000..1b6f361a4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabFragment.java @@ -0,0 +1,126 @@ +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 new file mode 100644 index 000000000..fc9a7595e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabModule.java @@ -0,0 +1,21 @@ +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 new file mode 100644 index 000000000..b60a4607b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exams/tab/ExamsTabPresenter.java @@ -0,0 +1,167 @@ +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 new file mode 100644 index 000000000..d2423ec8d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesContract.java @@ -0,0 +1,49 @@ +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 new file mode 100644 index 000000000..17ebc6e61 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesDialogFragment.java @@ -0,0 +1,117 @@ +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 new file mode 100644 index 000000000..ca1b949c7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesFragment.java @@ -0,0 +1,213 @@ +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 new file mode 100644 index 000000000..03f8564a4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesHeader.java @@ -0,0 +1,199 @@ +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 new file mode 100644 index 000000000..25eeef484 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesModule.java @@ -0,0 +1,23 @@ +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 new file mode 100644 index 000000000..0f4d363cb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesPresenter.java @@ -0,0 +1,230 @@ +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 new file mode 100644 index 000000000..ee9950406 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSubItem.java @@ -0,0 +1,162 @@ +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 new file mode 100644 index 000000000..a06fa0d55 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummaryHeader.java @@ -0,0 +1,87 @@ +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 new file mode 100644 index 000000000..58f418aa5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/grades/GradesSummarySubItem.java @@ -0,0 +1,84 @@ +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 new file mode 100644 index 000000000..0f2009beb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/settings/SettingsFragment.java @@ -0,0 +1,181 @@ +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 new file mode 100644 index 000000000..8344ad767 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableContract.java @@ -0,0 +1,33 @@ +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 new file mode 100644 index 000000000..cc668dc29 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableDialogFragment.java @@ -0,0 +1,127 @@ +package io.github.wulkanowy.ui.main.timetable; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.apache.commons.lang3.StringUtils; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import io.github.wulkanowy.R; +import io.github.wulkanowy.data.db.dao.entities.TimetableLesson; + +public class TimetableDialogFragment extends DialogFragment { + + private static final String ARGUMENT_KEY = "Item"; + + private TimetableLesson lesson; + + @BindView(R.id.timetable_dialog_lesson_value) + TextView lessonName; + + @BindView(R.id.timetable_dialog_teacher_value) + TextView teacher; + + @BindView(R.id.timetable_dialog_group_value) + TextView group; + + @BindView(R.id.timetable_dialog_room_value) + TextView room; + + @BindView(R.id.timetable_dialog_time_value) + TextView time; + + @BindView(R.id.timetable_dialog_description_value) + TextView description; + + @BindView(R.id.timetable_dialog_description) + View descriptionLabel; + + @BindView(R.id.timetable_dialog_teacher) + View teacherLabel; + + @BindView(R.id.timetable_dialog_group) + View groupLabel; + + public TimetableDialogFragment() { + //empty constructor for fragment + } + + public static TimetableDialogFragment newInstance(TimetableLesson lesson) { + TimetableDialogFragment dialogFragment = new TimetableDialogFragment(); + + Bundle bundle = new Bundle(); + bundle.putSerializable(ARGUMENT_KEY, lesson); + + dialogFragment.setArguments(bundle); + + return dialogFragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + lesson = (TimetableLesson) getArguments().getSerializable(ARGUMENT_KEY); + } + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.timetable_dialog, container, false); + + ButterKnife.bind(this, view); + + if (!lesson.getSubject().isEmpty()) { + lessonName.setText(lesson.getSubject()); + } + + if (!lesson.getTeacher().isEmpty()) { + teacher.setText(lesson.getTeacher()); + } else { + teacher.setVisibility(View.GONE); + teacherLabel.setVisibility(View.GONE); + } + + if (!lesson.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 new file mode 100644 index 000000000..db3cc83c4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableFragment.java @@ -0,0 +1,101 @@ +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 new file mode 100644 index 000000000..9ad1ee602 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetableModule.java @@ -0,0 +1,32 @@ +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 new file mode 100644 index 000000000..1e9cf0b81 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/TimetablePresenter.java @@ -0,0 +1,107 @@ +package io.github.wulkanowy.ui.main.timetable; + +import 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 new file mode 100644 index 000000000..cb877387e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableHeader.java @@ -0,0 +1,136 @@ +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 new file mode 100644 index 000000000..2bf47b335 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableSubItem.java @@ -0,0 +1,144 @@ +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 new file mode 100644 index 000000000..0814ff3c9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabContract.java @@ -0,0 +1,34 @@ +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 new file mode 100644 index 000000000..87db51951 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabFragment.java @@ -0,0 +1,143 @@ +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 new file mode 100644 index 000000000..1afb055ff --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabModule.java @@ -0,0 +1,21 @@ +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 new file mode 100644 index 000000000..f30dd804e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/timetable/tab/TimetableTabPresenter.java @@ -0,0 +1,200 @@ +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 deleted file mode 100644 index 8d07566f7..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt +++ /dev/null @@ -1,94 +0,0 @@ -package io.github.wulkanowy.ui.modules.about - -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.mikepenz.aboutlibraries.LibsBuilder -import com.mikepenz.aboutlibraries.LibsFragmentCompat -import io.github.wulkanowy.BuildConfig -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.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 deleted file mode 100644 index cc5ba7cf3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutModule.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.wulkanowy.ui.modules.about - -import com.mikepenz.aboutlibraries.LibsFragmentCompat -import dagger.Module -import dagger.Provides -import io.github.wulkanowy.di.scopes.PerFragment - -@Module -class AboutModule { - - @PerFragment - @Provides - fun provideLibsFragmentCompat() = LibsFragmentCompat() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt deleted file mode 100644 index 0b6db107c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.wulkanowy.ui.modules.about - -import com.mikepenz.aboutlibraries.Libs -import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL1 -import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL2 -import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL3 -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import timber.log.Timber -import javax.inject.Inject - -class AboutPresenter @Inject constructor( - errorHandler: ErrorHandler, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: AboutView) { - super.onAttachView(view) - Timber.i("About view 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 deleted file mode 100644 index 5b206ad82..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.wulkanowy.ui.modules.about - -import io.github.wulkanowy.ui.base.BaseView - -interface AboutView : BaseView { - - fun openDiscordInviteView() - - fun openEmailClientView() - - fun openHomepageWebView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt deleted file mode 100644 index 1eaa07c16..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt +++ /dev/null @@ -1,108 +0,0 @@ -package io.github.wulkanowy.ui.modules.account - -import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK -import android.content.Intent.FLAG_ACTIVITY_NEW_TASK -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import android.widget.Toast.LENGTH_LONG -import androidx.appcompat.app.AlertDialog -import dagger.android.support.DaggerAppCompatDialogFragment -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.dialog_account.* -import javax.inject.Inject - -class AccountDialog : DaggerAppCompatDialogFragment(), AccountView { - - @Inject - lateinit var presenter: AccountPresenter - - @Inject - lateinit var accountAdapter: FlexibleAdapter> - - companion object { - fun newInstance() = AccountDialog() - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_account, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this) - } - - override fun initView() { - accountAdapter.setOnItemClickListener { presenter.onItemSelected(it) } - - accountDialogAdd.setOnClickListener { presenter.onAddSelected() } - accountDialogRemove.setOnClickListener { presenter.onRemoveSelected() } - accountDialogRecycler.apply { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = accountAdapter - } - } - - override fun updateData(data: List) { - accountAdapter.updateDataSet(data) - } - - override fun showError(text: String, error: Throwable) { - showMessage(text) - } - - override fun showMessage(text: String) { - Toast.makeText(context, text, LENGTH_LONG).show() - } - - override fun dismissView() { - dismiss() - } - - override fun openLoginView() { - activity?.also { - startActivity(LoginActivity.getStartIntent(it)) - } - } - - override fun openClearLoginView() { - activity?.also { - startActivity(LoginActivity.getStartIntent(it) - .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) }) - } - } - - override fun showConfirmDialog() { - context?.let { - AlertDialog.Builder(it) - .setTitle(R.string.account_logout_student) - .setMessage(R.string.account_confirm) - .setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() } - .setNegativeButton(android.R.string.cancel) { _, _ -> } - .show() - } - } - - override fun 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 deleted file mode 100644 index 972d10a26..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt +++ /dev/null @@ -1,52 +0,0 @@ -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 deleted file mode 100644 index a16c544a8..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt +++ /dev/null @@ -1,103 +0,0 @@ -package io.github.wulkanowy.ui.modules.account - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.services.sync.SyncManager -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.SchedulersProvider -import io.reactivex.Single -import timber.log.Timber -import javax.inject.Inject - -class AccountPresenter @Inject constructor( - private val errorHandler: ErrorHandler, - private val studentRepository: StudentRepository, - private val syncManager: SyncManager, - private val schedulers: SchedulersProvider -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: AccountView) { - super.onAttachView(view) - 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 deleted file mode 100644 index 74662cf4e..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.ui.modules.account - -import io.github.wulkanowy.ui.base.BaseView - -interface AccountView : BaseView { - - fun initView() - - fun updateData(data: List) - - fun dismissView() - - fun showConfirmDialog() - - fun openLoginView() - - fun openClearLoginView() - - fun 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 deleted file mode 100644 index 611dd999e..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_attendance.* - -class AttendanceDialog : DialogFragment() { - - private lateinit var attendance: Attendance - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(exam: Attendance): AttendanceDialog { - return AttendanceDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - attendance = getSerializable(ARGUMENT_KEY) as Attendance - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_attendance, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - attendanceDialogSubject.text = attendance.subject - attendanceDialogDescription.text = attendance.name - attendanceDialogDate.text = attendance.date.toFormattedString() - attendanceDialogNumber.text = attendance.number.toString() - attendanceDialogClose.setOnClickListener { dismiss() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt deleted file mode 100644 index e5d8150d3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt +++ /dev/null @@ -1,158 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_attendance.* -import javax.inject.Inject - -class AttendanceFragment : BaseSessionFragment(), AttendanceView, MainView.MainChildView, MainView.TitledView { - - @Inject - lateinit var presenter: AttendancePresenter - - @Inject - lateinit var attendanceAdapter: FlexibleAdapter> - - companion object { - private const val SAVED_DATE_KEY = "CURRENT_DATE" - - fun newInstance() = AttendanceFragment() - } - - override val titleStringId: Int - get() = R.string.attendance_title - - override val isViewEmpty: Boolean - get() = attendanceAdapter.isEmpty - - override val currentStackSize: Int? - get() = (activity as? MainActivity)?.currentStackSize - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_attendance, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = attendanceRecycler - presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) - } - - override fun initView() { - attendanceAdapter.apply { - setOnItemClickListener { presenter.onAttendanceItemSelected(it) } - } - - attendanceRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = attendanceAdapter - addItemDecoration(FlexibleItemDecoration(context) - .withDefaultDivider() - .withDrawDividerOnLastItem(false) - ) - } - attendanceSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - attendancePreviousButton.setOnClickListener { presenter.onPreviousDay() } - attendanceNextButton.setOnClickListener { presenter.onNextDay() } - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.action_menu_attendance, menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return if (item.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected() - else false - } - - override fun updateData(data: List) { - attendanceAdapter.updateDataSet(data, true) - } - - override fun updateNavigationDay(date: String) { - attendanceNavDate.text = date - } - - override fun clearData() { - attendanceAdapter.clear() - } - - override fun resetView() { - attendanceRecycler.smoothScrollToPosition(0) - } - - override fun onFragmentReselected() { - 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 deleted file mode 100644 index 16a140cbf..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance - -import android.view.View -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Attendance -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_attendance.* - -class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_attendance - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - attendanceItemNumber.text = attendance.number.toString() - attendanceItemSubject.text = attendance.subject - attendanceItemDescription.text = attendance.name - attendanceItemAlert.visibility = attendance.run { if (absence && !excused) VISIBLE else INVISIBLE } - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as AttendanceItem - - if (attendance != other.attendance) return false - - return true - } - - override fun hashCode(): Int { - var result = attendance.hashCode() - result = 31 * result + attendance.id.toInt() - return result - } - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt deleted file mode 100644 index ec3d57e34..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ /dev/null @@ -1,141 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance - -import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.isHolidays -import io.github.wulkanowy.utils.nextSchoolDay -import io.github.wulkanowy.utils.previousOrSameSchoolDay -import io.github.wulkanowy.utils.previousSchoolDay -import io.github.wulkanowy.utils.toFormattedString -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDate.now -import org.threeten.bp.LocalDate.ofEpochDay -import timber.log.Timber -import java.util.concurrent.TimeUnit.MILLISECONDS -import javax.inject.Inject - -class AttendancePresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val attendanceRepository: AttendanceRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val prefRepository: PreferencesRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - lateinit var currentDate: LocalDate - private set - - fun onAttachView(view: AttendanceView, date: Long?) { - super.onAttachView(view) - 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, START_DATE to currentDate.toFormattedString("yyyy-MM-dd")) - }) { - Timber.i("Loading attendance result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - } - ) - } - } - - private fun reloadView() { - Timber.i("Reload attendance view with the date ${currentDate.toFormattedString()}") - view?.apply { - showProgress(true) - enableSwipe(false) - showContent(false) - showEmpty(false) - clearData() - showNextButton(!currentDate.plusDays(1).isHolidays) - showPreButton(!currentDate.minusDays(1).isHolidays) - updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize()) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt deleted file mode 100644 index ef3b874b6..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance - -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface AttendanceView : BaseSessionView { - - val isViewEmpty: Boolean - - val currentStackSize: Int? - - fun initView() - - fun updateData(data: List) - - fun updateNavigationDay(date: String) - - fun clearData() - - fun hideRefresh() - - fun resetView() - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun showPreButton(show: Boolean) - - fun showNextButton(show: Boolean) - - fun showAttendanceDialog(lesson: Attendance) - - fun openSummaryView() - - fun popView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt deleted file mode 100644 index b8d2c9528..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt +++ /dev/null @@ -1,125 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.summary - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import android.widget.ArrayAdapter -import android.widget.TextView -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnItemSelectedListener -import kotlinx.android.synthetic.main.fragment_attendance_summary.* -import javax.inject.Inject - -class AttendanceSummaryFragment : BaseSessionFragment(), AttendanceSummaryView, MainView.TitledView { - - @Inject - lateinit var presenter: AttendanceSummaryPresenter - - @Inject - lateinit var attendanceSummaryAdapter: FlexibleAdapter> - - private lateinit var subjectsAdapter: ArrayAdapter - - companion object { - private const val SAVED_SUBJECT_KEY = "CURRENT_SUBJECT" - - fun newInstance() = AttendanceSummaryFragment() - } - - override val titleStringId: Int - get() = R.string.attendance_title - - override val isViewEmpty - get() = attendanceSummaryAdapter.isEmpty - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_attendance_summary, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = attendanceSummaryRecycler - presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SUBJECT_KEY)) - } - - override fun initView() { - attendanceSummaryRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = attendanceSummaryAdapter - } - attendanceSummarySwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - - context?.let { - subjectsAdapter = ArrayAdapter(it, android.R.layout.simple_spinner_item, ArrayList()) - subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject) - } - - attendanceSummarySubjects.run { - adapter = subjectsAdapter - setOnItemSelectedListener { presenter.onSubjectSelected((it as TextView).text.toString()) } - } - } - - override fun updateSubjects(data: ArrayList) { - subjectsAdapter.run { - clear() - addAll(data) - notifyDataSetChanged() - } - } - - override fun updateDataSet(data: List, header: AttendanceSummaryScrollableHeader) { - attendanceSummaryAdapter.apply { - updateDataSet(data, true) - removeAllScrollableHeaders() - addScrollableHeader(header) - } - } - - override fun clearView() { - attendanceSummaryAdapter.clear() - } - - override fun showEmpty(show: Boolean) { - attendanceSummaryEmpty.visibility = if (show) VISIBLE else GONE - } - - override fun showProgress(show: Boolean) { - attendanceSummaryProgress.visibility = if (show) VISIBLE else GONE - } - - override fun enableSwipe(enable: Boolean) { - attendanceSummarySwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - attendanceSummaryRecycler.visibility = if (show) VISIBLE else GONE - } - - override fun showSubjects(show: Boolean) { - attendanceSummarySubjectsContainer.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun hideRefresh() { - attendanceSummarySwipe.isRefreshing = false - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putInt(SAVED_SUBJECT_KEY, presenter.currentSubjectId) - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt deleted file mode 100644 index 265d6ce44..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt +++ /dev/null @@ -1,80 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.summary - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_attendance_summary.* - -class AttendanceSummaryItem( - private val month: String, - private val percentage: String, - private val present: String, - private val absence: String, - private val excusedAbsence: String, - private val schoolAbsence: String, - private val exemption: String, - private val lateness: String, - private val excusedLateness: String -) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_attendance_summary - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - attendanceSummaryMonth.text = month - attendanceSummaryPercentage.text = percentage - attendanceSummaryPresent.text = present - attendanceSummaryAbsenceUnexcused.text = absence - attendanceSummaryAbsenceExcused.text = excusedAbsence - attendanceSummaryAbsenceSchool.text = schoolAbsence - attendanceSummaryExemption.text = exemption - attendanceSummaryLatenessUnexcused.text = lateness - attendanceSummaryLatenessExcused.text = excusedLateness - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as AttendanceSummaryItem - - if (month != other.month) return false - if (percentage != other.percentage) return false - if (present != other.present) return false - if (absence != other.absence) return false - if (excusedAbsence != other.excusedAbsence) return false - if (schoolAbsence != other.schoolAbsence) return false - if (exemption != other.exemption) return false - if (lateness != other.lateness) return false - if (excusedLateness != other.excusedLateness) return false - - return true - } - - override fun hashCode(): Int { - var result = month.hashCode() - result = 31 * result + percentage.hashCode() - result = 31 * result + present.hashCode() - result = 31 * result + absence.hashCode() - result = 31 * result + excusedAbsence.hashCode() - result = 31 * result + schoolAbsence.hashCode() - result = 31 * result + exemption.hashCode() - result = 31 * result + lateness.hashCode() - result = 31 * result + excusedLateness.hashCode() - return result - } - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt deleted file mode 100644 index 09ad8980f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt +++ /dev/null @@ -1,140 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.summary - -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.github.wulkanowy.data.db.entities.Subject -import io.github.wulkanowy.data.repositories.attendancesummary.AttendanceSummaryRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.subject.SubjectRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.calculatePercentage -import io.github.wulkanowy.utils.getFormattedName -import timber.log.Timber -import java.lang.String.format -import java.util.Locale.FRANCE -import java.util.concurrent.TimeUnit.MILLISECONDS -import javax.inject.Inject - -class AttendanceSummaryPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val attendanceSummaryRepository: AttendanceSummaryRepository, - private val subjectRepository: SubjectRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val schedulers: SchedulersProvider, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - private var subjects = emptyList() - - var currentSubjectId = -1 - private set - - fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) { - super.onAttachView(view) - 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 deleted file mode 100644 index c258f71d2..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryScrollableHeader.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.summary - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.scrollable_header_attendance_summary.* - -class AttendanceSummaryScrollableHeader(private val percentage: String) : - AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.scrollable_header_attendance_summary - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder?, position: Int, payloads: MutableList?) { - holder?.apply { attendanceSummaryScrollableHeaderPercentage.text = percentage } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as AttendanceSummaryScrollableHeader - - if (percentage != other.percentage) return false - - return true - } - - override fun hashCode(): Int { - return percentage.hashCode() - } - - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt deleted file mode 100644 index e4c36db7b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.summary - -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface AttendanceSummaryView : BaseSessionView { - - val isViewEmpty: Boolean - - fun initView() - - fun hideRefresh() - - fun showContent(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showEmpty(show: Boolean) - - fun updateDataSet(data: List, header: AttendanceSummaryScrollableHeader) - - fun updateSubjects(data: ArrayList) - - fun showSubjects(show: Boolean) - - fun clearView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt deleted file mode 100644 index ed5092c96..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.modules.exam - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_exam.* - -class ExamDialog : DialogFragment() { - - private lateinit var exam: Exam - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(exam: Exam): ExamDialog { - return ExamDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - exam = getSerializable(ARGUMENT_KEY) as Exam - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_exam, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - examDialogSubjectValue.text = exam.subject - examDialogTypeValue.text = exam.type - examDialogTeacherValue.text = exam.teacher - examDialogDateValue.text = exam.entryDate.toFormattedString() - examDialogDescriptionValue.text = exam.description - - examDialogClose.setOnClickListener { dismiss() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt deleted file mode 100644 index 8f9576f2a..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt +++ /dev/null @@ -1,131 +0,0 @@ -package io.github.wulkanowy.ui.modules.exam - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_exam.* -import javax.inject.Inject - -class ExamFragment : BaseSessionFragment(), ExamView, MainView.MainChildView, MainView.TitledView { - - @Inject - lateinit var presenter: ExamPresenter - - @Inject - lateinit var examAdapter: FlexibleAdapter> - - companion object { - private const val SAVED_DATE_KEY = "CURRENT_DATE" - - fun newInstance() = ExamFragment() - } - - override val titleStringId: Int - get() = R.string.exam_title - - override val isViewEmpty: Boolean - get() = examAdapter.isEmpty - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_exam, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = examRecycler - presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) - } - - override fun initView() { - examAdapter.run { - setOnItemClickListener { presenter.onExamItemSelected(it) } - } - examRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = examAdapter - addItemDecoration(FlexibleItemDecoration(context) - .withDefaultDivider(R.layout.item_exam) - .withDrawDividerOnLastItem(false) - ) - } - examSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - examPreviousButton.setOnClickListener { presenter.onPreviousWeek() } - examNextButton.setOnClickListener { presenter.onNextWeek() } - } - - override fun hideRefresh() { - examSwipe.isRefreshing = false - } - - override fun updateData(data: List) { - examAdapter.updateDataSet(data, true) - } - - override fun updateNavigationWeek(date: String) { - examNavDate.text = date - } - - override fun clearData() { - examAdapter.clear() - } - - override fun resetView() { - examRecycler.scrollToPosition(0) - } - - override fun onFragmentReselected() { - 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 deleted file mode 100644 index 0a5b862c3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamHeader.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.ui.modules.exam - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractHeaderItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.ExpandableViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.utils.toFormattedString -import io.github.wulkanowy.utils.weekDayName -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.header_exam.* -import org.threeten.bp.LocalDate - -class ExamHeader(private val date: LocalDate) : AbstractHeaderItem() { - - override fun getLayoutRes() = R.layout.header_exam - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder, - position: Int, payloads: MutableList?) { - holder.run { - examHeaderDay.text = date.weekDayName.capitalize() - examHeaderDate.text = date.toFormattedString() - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as ExamHeader - - if (date != other.date) return false - - return true - } - - override fun hashCode(): Int { - return date.hashCode() - } - - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : ExpandableViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt deleted file mode 100644 index 8971b4df3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.modules.exam - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractSectionableItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Exam -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_exam.* - -class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem(header) { - - override fun getLayoutRes() = R.layout.item_exam - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.run { - examItemSubject.text = exam.subject - examItemTeacher.text = exam.teacher - examItemType.text = exam.type - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as ExamItem - - if (exam != other.exam) return false - - return true - } - - override fun hashCode(): Int { - var result = exam.hashCode() - result = 31 * result + exam.id.toInt() - return result - } - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt deleted file mode 100644 index a18f7b2a4..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt +++ /dev/null @@ -1,136 +0,0 @@ -package io.github.wulkanowy.ui.modules.exam - -import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.repositories.exam.ExamRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.isHolidays -import io.github.wulkanowy.utils.monday -import io.github.wulkanowy.utils.nextOrSameSchoolDay -import io.github.wulkanowy.utils.toFormattedString -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDate.now -import org.threeten.bp.LocalDate.ofEpochDay -import timber.log.Timber -import java.util.concurrent.TimeUnit.MILLISECONDS -import javax.inject.Inject - -class ExamPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val examRepository: ExamRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - lateinit var currentDate: LocalDate - private set - - fun onAttachView(view: ExamView, date: Long?) { - super.onAttachView(view) - 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, START_DATE to currentDate.toFormattedString("yyyy-MM-dd")) - }) { - Timber.i("Loading exam result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } - } - - private fun createExamItems(items: Map>): List { - return items.flatMap { - ExamHeader(it.key).let { header -> - it.value.reversed().map { item -> ExamItem(header, item) } - } - } - } - - private fun reloadView() { - Timber.i("Reload exam view with the date ${currentDate.toFormattedString()}") - view?.apply { - showProgress(true) - enableSwipe(false) - showContent(false) - showEmpty(false) - clearData() - showPreButton(!currentDate.minusDays(7).isHolidays) - showNextButton(!currentDate.plusDays(7).isHolidays) - updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " + - currentDate.friday.toFormattedString("dd.MM")) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt deleted file mode 100644 index 2ced3f2d4..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.ui.modules.exam - -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface ExamView : BaseSessionView { - - val isViewEmpty: Boolean - - fun initView() - - fun updateData(data: List) - - fun updateNavigationWeek(date: String) - - fun clearData() - - fun hideRefresh() - - fun resetView() - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun showNextButton(show: Boolean) - - fun showPreButton(show: Boolean) - - fun showExamDialog(exam: Exam) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/CustomTabLayout.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/CustomTabLayout.kt deleted file mode 100644 index e6f01497c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/CustomTabLayout.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade - -import android.content.Context -import android.util.AttributeSet -import android.view.ViewGroup -import com.google.android.material.tabs.TabLayout - -/** - * @see Tabs don't fit to screen with tabmode=scrollable, Even with a Custom Tab Layout - */ -class CustomTabLayout : TabLayout { - - constructor(context: Context) : super(context) - - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) - - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - - override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - setMeasuredDimension(widthMeasureSpec, heightMeasureSpec) - val tabLayout = getChildAt(0) as ViewGroup - val childCount = tabLayout.childCount - - if (childCount == 0) return - - val tabMinWidth = context.resources.displayMetrics.widthPixels / childCount - - for (i in 0 until childCount) { - tabLayout.getChildAt(i).minimumWidth = tabMinWidth - } - - super.onMeasure(widthMeasureSpec, heightMeasureSpec) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt deleted file mode 100644 index a76f3d5ac..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt +++ /dev/null @@ -1,54 +0,0 @@ -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.preferences.PreferencesRepository -import io.github.wulkanowy.utils.calcAverage -import io.github.wulkanowy.utils.changeModifier -import io.reactivex.Single -import javax.inject.Inject - -class GradeAverageProvider @Inject constructor( - private val preferencesRepository: PreferencesRepository, - private val gradeRepository: GradeRepository -) { - 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 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 gradeRepository.getGrades(student, selectedSemester, forceRefresh) - .map { grades -> - grades.map { it.changeModifier(plusModifier, minusModifier) } - .groupBy { it.subject } - .mapValues { it.value.calcAverage() } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt deleted file mode 100644 index 182258d14..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt +++ /dev/null @@ -1,164 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import androidx.appcompat.app.AlertDialog -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment -import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment -import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnSelectPageListener -import kotlinx.android.synthetic.main.fragment_grade.* -import javax.inject.Inject - -class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView, MainView.TitledView { - - @Inject - lateinit var presenter: GradePresenter - - @Inject - lateinit var pagerAdapter: BaseFragmentPagerAdapter - - private var semesterSwitchMenu: MenuItem? = null - - companion object { - private const val SAVED_SEMESTER_KEY = "CURRENT_SEMESTER" - - fun newInstance() = GradeFragment() - } - - override val titleStringId: Int - get() = R.string.grade_title - - override val currentPageIndex: Int - get() = gradeViewPager.currentItem - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_grade, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SEMESTER_KEY)) - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.action_menu_grade, menu) - semesterSwitchMenu = menu.findItem(R.id.gradeMenuSemester) - presenter.onCreateMenu() - } - - override fun initView() { - pagerAdapter.apply { - containerId = gradeViewPager.id - addFragmentsWithTitle(mapOf( - GradeDetailsFragment.newInstance() to getString(R.string.all_details), - GradeSummaryFragment.newInstance() to getString(R.string.grade_menu_summary), - GradeStatisticsFragment.newInstance() to getString(R.string.grade_menu_statistics) - )) - } - - gradeViewPager.run { - adapter = pagerAdapter - offscreenPageLimit = 3 - setOnSelectPageListener { presenter.onPageSelected(it) } - } - gradeTabLayout.setupWithViewPager(gradeViewPager) - gradeSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return if (item.itemId == R.id.gradeMenuSemester) presenter.onSemesterSwitch() - else false - } - - override fun onFragmentReselected() { - 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 deleted file mode 100644 index 46a923d9a..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeModule.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade - -import dagger.Module -import dagger.Provides -import dagger.android.ContributesAndroidInjector -import io.github.wulkanowy.di.scopes.PerChildFragment -import io.github.wulkanowy.di.scopes.PerFragment -import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment -import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment -import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment - -@Module -abstract class GradeModule { - - @Module - companion object { - - @JvmStatic - @PerFragment - @Provides - fun provideGradeAdapter(fragment: GradeFragment) = BaseFragmentPagerAdapter(fragment.childFragmentManager) - } - - @PerChildFragment - @ContributesAndroidInjector - abstract fun bindGradeDetailsFragment(): GradeDetailsFragment - - @PerChildFragment - @ContributesAndroidInjector - abstract fun binGradeSummaryFragment(): GradeSummaryFragment - - @PerChildFragment - @ContributesAndroidInjector - abstract fun binGradeStatisticsFragment(): GradeStatisticsFragment -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt deleted file mode 100644 index 6a8d4d580..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt +++ /dev/null @@ -1,130 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import timber.log.Timber -import javax.inject.Inject - -class GradePresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - var selectedIndex = 0 - private set - - private var semesters = emptyList() - - private val loadedSemesterId = mutableMapOf() - - fun onAttachView(view: GradeView, savedIndex: Int?) { - super.onAttachView(view) - 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 deleted file mode 100644 index 9fdd46b16..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade - -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface GradeView : BaseSessionView { - - val currentPageIndex: Int - - fun initView() - - fun showContent(show: Boolean) - - fun showProgress(show: Boolean) - - fun showEmpty(show: Boolean) - - fun showRefresh(show: Boolean) - - fun showSemesterSwitch(show: Boolean) - - fun showSemesterDialog(selectedIndex: Int) - - fun enableSwipe(enable: Boolean) - - fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) - - fun notifyChildParentReselected(index: Int) - - fun notifyChildSemesterChange(index: Int) - - interface GradeChildView { - - fun onParentChangeSemester() - - fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) - - fun onParentReselected() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt deleted file mode 100644 index 0abeaeea0..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt +++ /dev/null @@ -1,84 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.utils.colorStringId -import io.github.wulkanowy.utils.getBackgroundColor -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_grade.* - -class GradeDetailsDialog : DialogFragment() { - - private lateinit var grade: Grade - - private lateinit var colorScheme: String - - companion object { - private const val ARGUMENT_KEY = "Item" - private const val COLOR_SCHEME_KEY = "Scheme" - - fun newInstance(grade: Grade, colorScheme: String): GradeDetailsDialog { - return GradeDetailsDialog().apply { - arguments = Bundle().apply { - putSerializable(ARGUMENT_KEY, grade) - putString(COLOR_SCHEME_KEY, colorScheme) - } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - grade = getSerializable(ARGUMENT_KEY) as Grade - colorScheme = getString(COLOR_SCHEME_KEY) ?: "default" - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_grade, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - gradeDialogSubject.text = grade.subject - gradeDialogWeightValue.text = grade.weight - gradeDialogDateValue.text = grade.date.toFormattedString() - gradeDialogColorValue.text = getString(grade.colorStringId) - - gradeDialogCommentValue.apply { - if (grade.comment.isBlank()) { - visibility = GONE - gradeDialogComment.visibility = GONE - } else text = grade.comment - } - - gradeDialogValue.run { - text = grade.entry - setBackgroundResource(grade.getBackgroundColor(colorScheme)) - } - - gradeDialogTeacherValue.text = if (grade.teacher.isBlank()) { - getString(R.string.all_no_data) - } else grade.teacher - - gradeDialogDescriptionValue.text = grade.run { - when { - description.isBlank() && gradeSymbol.isNotBlank() -> gradeSymbol - description.isBlank() && gradeSymbol.isBlank() -> getString(R.string.all_no_description) - gradeSymbol.isNotBlank() && description.isNotBlank() -> "$gradeSymbol - $description" - else -> description - } - } - - gradeDialogClose.setOnClickListener { dismiss() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt deleted file mode 100644 index 65cd9262d..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt +++ /dev/null @@ -1,172 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.View.GONE -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IExpandable -import eu.davidea.flexibleadapter.items.IFlexible -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.grade.GradeFragment -import io.github.wulkanowy.ui.modules.grade.GradeView -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_grade_details.* -import javax.inject.Inject - -class GradeDetailsFragment : BaseSessionFragment(), GradeDetailsView, GradeView.GradeChildView { - - @Inject - lateinit var presenter: GradeDetailsPresenter - - @Inject - lateinit var gradeDetailsAdapter: FlexibleAdapter> - - companion object { - fun newInstance() = GradeDetailsFragment() - } - - override val emptyAverageString: String - get() = getString(R.string.grade_no_average) - - override val averageString: String - get() = getString(R.string.grade_average) - - override val weightString: String - get() = getString(R.string.grade_weight) - - override val noDescriptionString: String - get() = getString(R.string.all_no_description) - - override val isViewEmpty - get() = gradeDetailsAdapter.isEmpty - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_grade_details, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = gradeDetailsRecycler - presenter.onAttachView(this) - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.action_menu_grade_details, menu) - } - - override fun initView() { - gradeDetailsAdapter.run { - isAutoCollapseOnExpand = true - isAutoScrollOnExpand = true - setOnItemClickListener { presenter.onGradeItemSelected(it) } - } - - gradeDetailsRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = gradeDetailsAdapter - addItemDecoration(GradeDetailsHeaderItemDecoration(context) - .withDefaultDivider(R.layout.header_grade_details) - ) - } - gradeDetailsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return if (item.itemId == R.id.gradeDetailsMenuRead) presenter.onMarkAsReadSelected() - else false - } - - override fun updateData(data: List) { - gradeDetailsAdapter.updateDataSet(data, true) - } - - override fun updateItem(item: AbstractFlexibleItem<*>) { - gradeDetailsAdapter.updateItem(item) - } - - override fun clearView() { - gradeDetailsAdapter.clear() - } - - override fun collapseAllItems() { - gradeDetailsAdapter.collapseAll() - } - - override fun scrollToStart() { - gradeDetailsRecycler.scrollToPosition(0) - } - - override fun getHeaderOfItem(item: AbstractFlexibleItem<*>): IExpandable<*, out IFlexible<*>>? { - return gradeDetailsAdapter.getExpandableOf(item) - } - - override fun getGradeNumberString(number: Int): String { - return resources.getQuantityString(R.plurals.grade_number_item, number, number) - } - - override fun showProgress(show: Boolean) { - gradeDetailsProgress.visibility = if (show) VISIBLE else GONE - } - - override fun enableSwipe(enable: Boolean) { - gradeDetailsSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - gradeDetailsRecycler.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showEmpty(show: Boolean) { - gradeDetailsEmpty.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showRefresh(show: Boolean) { - gradeDetailsSwipe.isRefreshing = show - } - - override fun showGradeDialog(grade: Grade, colorScheme: String) { - (activity as? MainActivity)?.showDialogFragment(GradeDetailsDialog.newInstance(grade, colorScheme)) - } - - override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { - presenter.onParentViewLoadData(semesterId, forceRefresh) - } - - override fun onParentReselected() { - presenter.onParentViewReselected() - } - - override fun onParentChangeSemester() { - presenter.onParentViewChangeSemester() - } - - override fun notifyParentDataLoaded(semesterId: Int) { - (parentFragment as? GradeFragment)?.onChildFragmentLoaded(semesterId) - } - - override fun notifyParentRefresh() { - (parentFragment as? GradeFragment)?.onChildRefresh() - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt deleted file mode 100644 index e5f6e8249..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt +++ /dev/null @@ -1,90 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractExpandableItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.ExpandableViewHolder -import io.github.wulkanowy.R -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.header_grade_details.* - -class GradeDetailsHeader( - private val subject: String, - private val number: String, - private val average: String, - var newGrades: Int, - private val isExpandable: Boolean -) : AbstractExpandableItem() { - - init { - isExpanded = !isExpandable - } - - override fun getLayoutRes() = R.layout.header_grade_details - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.run { - gradeHeaderSubject.apply { - text = subject - maxLines = if (isExpanded) 2 else 1 - } - gradeHeaderAverage.text = average - gradeHeaderNumber.text = number - gradeHeaderNote.visibility = if (newGrades > 0) VISIBLE else GONE - gradeHeaderContainer.isEnabled = isExpandable - - isViewExpandable = isExpandable - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as GradeDetailsHeader - - if (subject != other.subject) return false - if (number != other.number) return false - if (average != other.average) return false - if (isExpandable != other.isExpandable) return false - - return true - } - - override fun hashCode(): Int { - var result = subject.hashCode() - result = 31 * result + number.hashCode() - result = 31 * result + average.hashCode() - result = 31 * result + isExpandable.hashCode() - return result - } - - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : - ExpandableViewHolder(view, adapter), LayoutContainer { - - var isViewExpandable = true - - init { - contentView.setOnClickListener(this) - } - - override val containerView: View - get() = contentView - - override fun isViewCollapsibleOnClick() = isViewExpandable - - override fun isViewExpandableOnClick() = isViewExpandable - - override fun onClick(view: View?) { - super.onClick(view) - mAdapter.getItem(adapterPosition)?.let { mAdapter.updateItem(it) } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeaderItemDecoration.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeaderItemDecoration.kt deleted file mode 100644 index 39a911e62..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeaderItemDecoration.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import android.content.Context -import android.graphics.Canvas -import androidx.recyclerview.widget.RecyclerView -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration - -class GradeDetailsHeaderItemDecoration(context: Context) : FlexibleItemDecoration(context) { - - override fun drawVertical(canvas: Canvas, parent: RecyclerView) { - canvas.save() - val left: Int - val right: Int - if (parent.clipToPadding) { - left = parent.paddingLeft - right = parent.width - parent.paddingRight - canvas.clipRect(left, parent.paddingTop, right, - parent.height - parent.paddingBottom) - } else { - left = 0 - right = parent.width - } - - val itemCount = parent.childCount - for (i in 1 until itemCount) { - val child = parent.getChildAt(i) - val viewHolder = parent.getChildViewHolder(child) - if (shouldDrawDivider(viewHolder)) { - parent.getDecoratedBoundsWithMargins(child, mBounds) - val bottom = mBounds.top + Math.round(child.translationY) - val top = bottom - mDivider.intrinsicHeight - mDivider.setBounds(left, top, right, bottom) - mDivider.draw(canvas) - } - } - canvas.restore() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt deleted file mode 100644 index 1e47eca5d..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt +++ /dev/null @@ -1,74 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import android.annotation.SuppressLint -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_grade_details.* - -class GradeDetailsItem( - val grade: Grade, - private val valueBgColor: Int, - private val weightString: String, - private val noDescriptionString: String -) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_grade_details - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.run { - gradeItemValue.run { - text = grade.entry - setBackgroundResource(valueBgColor) - } - gradeItemDescription.text = when { - grade.description.isNotBlank() -> grade.description - grade.gradeSymbol.isNotBlank() -> grade.gradeSymbol - else -> noDescriptionString - } - gradeItemDate.text = grade.date.toFormattedString() - gradeItemWeight.text = "$weightString: ${grade.weight}" - gradeItemNote.visibility = if (!grade.isRead) VISIBLE else GONE - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as GradeDetailsItem - - if (grade != other.grade) return false - if (grade.id != other.grade.id) return false - if (weightString != other.weightString) return false - if (valueBgColor != other.valueBgColor) return false - - return true - } - - override fun hashCode(): Int { - var result = grade.hashCode() - result = 31 * result + grade.id.toInt() - result = 31 * result + weightString.hashCode() - result = 31 * result + valueBgColor - return result - } - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt deleted file mode 100644 index 12e606873..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ /dev/null @@ -1,192 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.repositories.grade.GradeRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.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( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val gradeRepository: GradeRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val preferencesRepository: PreferencesRepository, - private val averageProvider: GradeAverageProvider, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - private var currentSemesterId = 0 - - override fun onAttachView(view: GradeDetailsView) { - super.onAttachView(view) - view.initView() - } - - fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { - currentSemesterId = semesterId - loadData(semesterId, forceRefresh) - } - - fun onGradeItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is GradeDetailsItem) { - Timber.i("Select grade item ${item.grade.id}") - view?.apply { - showGradeDialog(item.grade, preferencesRepository.gradeColorTheme) - if (!item.grade.isRead) { - item.grade.isRead = true - updateItem(item) - getHeaderOfItem(item)?.let { header -> - if (header is GradeDetailsHeader) { - header.newGrades-- - updateItem(header) - } - } - updateGrade(item.grade) - } - } - } - } - - fun onMarkAsReadSelected(): Boolean { - Timber.i("Select mark grades as read") - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it) } - .flatMap { gradeRepository.getUnreadGrades(it.first { item -> item.semesterId == currentSemesterId }) } - .map { it.map { grade -> grade.apply { isRead = true } } } - .flatMapCompletable { - Timber.i("Mark as read ${it.size} grades") - gradeRepository.updateGrades(it) - } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ - Timber.i("Mark as read result: Success") - loadData(currentSemesterId, false) - }, { - Timber.i("Mark as read result: An exception occurred") - errorHandler.dispatch(it) - })) - return true - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the grade details") - view?.notifyParentRefresh() - } - - fun onParentViewReselected() { - view?.run { - if (!isViewEmpty) { - if (preferencesRepository.isGradeExpandable) collapseAllItems() - scrollToStart() - } - } - } - - fun onParentViewChangeSemester() { - view?.run { - showProgress(true) - enableSwipe(false) - showRefresh(false) - showContent(false) - showEmpty(false) - clearView() - } - disposable.clear() - } - - private fun loadData(semesterId: Int, forceRefresh: Boolean) { - Timber.i("Loading grade details data started") - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it).map { semester -> 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 deleted file mode 100644 index 1fb98216b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.details - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IExpandable -import eu.davidea.flexibleadapter.items.IFlexible -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface GradeDetailsView : BaseSessionView { - - val isViewEmpty: Boolean - - val emptyAverageString: String - - val averageString: String - - val weightString: String - - val noDescriptionString: String - - fun initView() - - fun updateData(data: List) - - fun updateItem(item: AbstractFlexibleItem<*>) - - fun clearView() - - fun scrollToStart() - - fun collapseAllItems() - - fun showGradeDialog(grade: Grade, colorScheme: String) - - fun showContent(show: Boolean) - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showRefresh(show: Boolean) - - fun notifyParentDataLoaded(semesterId: Int) - - fun notifyParentRefresh() - - fun getGradeNumberString(number: Int): String - - fun getHeaderOfItem(item: AbstractFlexibleItem<*>): IExpandable<*, out IFlexible<*>>? -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt deleted file mode 100644 index f27a13c83..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt +++ /dev/null @@ -1,214 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.statistics - -import android.graphics.Color.WHITE -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ArrayAdapter -import android.widget.TextView -import androidx.core.content.ContextCompat -import com.github.mikephil.charting.components.Legend -import com.github.mikephil.charting.components.LegendEntry -import com.github.mikephil.charting.data.PieData -import com.github.mikephil.charting.data.PieDataSet -import com.github.mikephil.charting.data.PieEntry -import com.github.mikephil.charting.formatter.ValueFormatter -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.grade.GradeFragment -import io.github.wulkanowy.ui.modules.grade.GradeView -import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.setOnItemSelectedListener -import kotlinx.android.synthetic.main.fragment_grade_statistics.* -import javax.inject.Inject - -class GradeStatisticsFragment : BaseSessionFragment(), GradeStatisticsView, GradeView.GradeChildView { - - @Inject - lateinit var presenter: GradeStatisticsPresenter - - private lateinit var subjectsAdapter: ArrayAdapter - - companion object { - private const val SAVED_CHART_TYPE = "CURRENT_TYPE" - - fun newInstance() = GradeStatisticsFragment() - } - - override val isViewEmpty - get() = gradeStatisticsChart.isEmpty - - private lateinit var gradeColors: List> - - private val vulcanGradeColors = listOf( - 6 to R.color.grade_vulcan_six, - 5 to R.color.grade_vulcan_five, - 4 to R.color.grade_vulcan_four, - 3 to R.color.grade_vulcan_three, - 2 to R.color.grade_vulcan_two, - 1 to R.color.grade_vulcan_one - ) - - private val materialGradeColors = listOf( - 6 to R.color.grade_material_six, - 5 to R.color.grade_material_five, - 4 to R.color.grade_material_four, - 3 to R.color.grade_material_three, - 2 to R.color.grade_material_two, - 1 to R.color.grade_material_one - ) - - private val gradeLabels = listOf( - "6, 6-", "5, 5-, 5+", "4, 4-, 4+", "3, 3-, 3+", "2, 2-, 2+", "1, 1+" - ) - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_grade_statistics, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = gradeStatisticsChart - presenter.onAttachView(this, savedInstanceState?.getBoolean(SAVED_CHART_TYPE)) - } - - override fun initView() { - gradeStatisticsChart.run { - description.isEnabled = false - setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground)) - setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary)) - animateXY(1000, 1000) - minAngleForSlices = 25f - legend.apply { - textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary) - } - } - - context?.let { - subjectsAdapter = ArrayAdapter(it, android.R.layout.simple_spinner_item, ArrayList()) - subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject) - } - - gradeStatisticsSubjects.run { - adapter = subjectsAdapter - setOnItemSelectedListener { presenter.onSubjectSelected((it as TextView).text.toString()) } - } - - gradeStatisticsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun updateSubjects(data: ArrayList) { - subjectsAdapter.run { - clear() - addAll(data) - notifyDataSetChanged() - } - } - - override fun updateData(items: List, theme: String) { - gradeColors = when (theme) { - "vulcan" -> vulcanGradeColors - else -> materialGradeColors - } - - gradeStatisticsChart.run { - data = PieData(PieDataSet(items.map { - PieEntry(it.amount.toFloat(), it.grade.toString()) - }, "Legenda").apply { - valueTextSize = 12f - sliceSpace = 1f - valueTextColor = WHITE - setColors(items.map { - gradeColors.single { color -> color.first == it.grade }.second - }.toIntArray(), context) - }).apply { - setTouchEnabled(false) - setValueFormatter(object : ValueFormatter() { - override fun getPieLabel(value: Float, pieEntry: PieEntry): String { - return resources.getQuantityString(R.plurals.grade_number_item, value.toInt(), value.toInt()) - } - }) - centerText = items.fold(0) { acc, it -> acc + it.amount } - .let { resources.getQuantityString(R.plurals.grade_number_item, it, it) } - } - legend.apply { - setCustom(gradeLabels.mapIndexed { i, it -> - LegendEntry().apply { - label = it - formColor = ContextCompat.getColor(context, gradeColors[i].second) - form = Legend.LegendForm.SQUARE - } - }) - } - invalidate() - } - } - - override fun showSubjects(show: Boolean) { - gradeStatisticsSubjectsContainer.visibility = if (show) View.VISIBLE else View.INVISIBLE - gradeStatisticsTypeSwitch.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun clearView() { - gradeStatisticsChart.clear() - } - - override fun showContent(show: Boolean) { - gradeStatisticsChart.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showEmpty(show: Boolean) { - gradeStatisticsEmpty.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showProgress(show: Boolean) { - gradeStatisticsProgress.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun enableSwipe(enable: Boolean) { - gradeStatisticsSwipe.isEnabled = enable - } - - override fun showRefresh(show: Boolean) { - gradeStatisticsSwipe.isRefreshing = show - } - - override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { - presenter.onParentViewLoadData(semesterId, forceRefresh) - } - - override fun onParentReselected() { - // - } - - override fun onParentChangeSemester() { - presenter.onParentViewChangeSemester() - } - - override fun notifyParentDataLoaded(semesterId: Int) { - (parentFragment as? GradeFragment)?.onChildFragmentLoaded(semesterId) - } - - override fun notifyParentRefresh() { - (parentFragment as? GradeFragment)?.onChildRefresh() - } - - override fun onResume() { - super.onResume() - gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId -> - presenter.onTypeChange(checkedId == R.id.gradeStatisticsTypeSemester) - } - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putBoolean(GradeStatisticsFragment.SAVED_CHART_TYPE, presenter.currentIsSemester) - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt deleted file mode 100644 index 5dd485cd5..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt +++ /dev/null @@ -1,147 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.statistics - -import io.github.wulkanowy.data.db.entities.Subject -import io.github.wulkanowy.data.repositories.gradestatistics.GradeStatisticsRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.subject.SubjectRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import timber.log.Timber -import javax.inject.Inject - -class GradeStatisticsPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val gradeStatisticsRepository: GradeStatisticsRepository, - private val subjectRepository: SubjectRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val preferencesRepository: PreferencesRepository, - private val schedulers: SchedulersProvider, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - private var subjects = emptyList() - - private var currentSemesterId = 0 - - private var currentSubjectName: String = "Wszystkie" - - var currentIsSemester = false - private set - - fun onAttachView(view: GradeStatisticsView, isSemester: Boolean?) { - super.onAttachView(view) - currentIsSemester = isSemester ?: false - view.initView() - } - - fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { - currentSemesterId = semesterId - loadSubjects() - loadData(semesterId, currentSubjectName, currentIsSemester, forceRefresh) - } - - fun onParentViewChangeSemester() { - view?.run { - showProgress(true) - enableSwipe(false) - showRefresh(false) - showContent(false) - showEmpty(false) - clearView() - } - disposable.clear() - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the grade stats") - view?.notifyParentRefresh() - } - - fun onSubjectSelected(name: String) { - Timber.i("Select grade stats subject $name") - view?.run { - showContent(false) - showProgress(true) - enableSwipe(false) - showEmpty(false) - clearView() - } - (subjects.singleOrNull { it.name == name }?.name).let { - if (it != currentSubjectName) loadData(currentSemesterId, name, currentIsSemester) - } - } - - fun onTypeChange(isSemester: Boolean) { - Timber.i("Select grade stats semester: $isSemester") - disposable.clear() - view?.run { - showContent(false) - showProgress(true) - enableSwipe(false) - showEmpty(false) - clearView() - } - loadData(currentSemesterId, currentSubjectName, isSemester) - } - - private fun loadSubjects() { - Timber.i("Loading grade stats subjects started") - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { subjectRepository.getSubjects(it) } - .doOnSuccess { subjects = it } - .map { ArrayList(it.map { subject -> subject.name }) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ - Timber.i("Loading grade stats subjects result: Success") - view?.run { - updateSubjects(it) - showSubjects(true) - } - }, { - Timber.e("Loading grade stats subjects result: An exception occurred") - errorHandler.dispatch(it) - }) - ) - } - - private fun loadData(semesterId: Int, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false) { - Timber.i("Loading grade stats data started") - currentSubjectName = subjectName - currentIsSemester = isSemester - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it) } - .flatMap { gradeStatisticsRepository.getGradesStatistics(it.first { item -> item.semesterId == semesterId }, subjectName, isSemester, forceRefresh) } - .map { list -> list.sortedByDescending { it.grade } } - .map { list -> list.filter { it.amount != 0 } } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - showRefresh(false) - showProgress(false) - enableSwipe(true) - notifyParentDataLoaded(semesterId) - } - } - .subscribe({ - Timber.i("Loading grade stats result: Success") - view?.run { - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - updateData(it, preferencesRepository.gradeColorTheme) - } - analytics.logEvent("load_grade_statistics", "items" to it.size, "force_refresh" to forceRefresh) - }) { - Timber.e("Loading grade stats result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt deleted file mode 100644 index dbdde459e..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.statistics - -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface GradeStatisticsView : BaseSessionView { - - val isViewEmpty: Boolean - - fun initView() - - fun updateSubjects(data: ArrayList) - - fun updateData(items: List, theme: String) - - fun showSubjects(show: Boolean) - - fun notifyParentDataLoaded(semesterId: Int) - - fun notifyParentRefresh() - - fun clearView() - - fun showContent(show: Boolean) - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showRefresh(show: Boolean) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt deleted file mode 100644 index f174f96c2..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt +++ /dev/null @@ -1,121 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.summary - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.grade.GradeFragment -import io.github.wulkanowy.ui.modules.grade.GradeView -import kotlinx.android.synthetic.main.fragment_grade_summary.* -import javax.inject.Inject - -class GradeSummaryFragment : BaseSessionFragment(), GradeSummaryView, GradeView.GradeChildView { - - @Inject - lateinit var presenter: GradeSummaryPresenter - - @Inject - lateinit var gradeSummaryAdapter: FlexibleAdapter> - - companion object { - fun newInstance() = GradeSummaryFragment() - } - - override val isViewEmpty - get() = gradeSummaryAdapter.isEmpty - - override val predictedString - get() = getString(R.string.grade_summary_predicted_grade) - - override val finalString - get() = getString(R.string.grade_summary_final_grade) - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_grade_summary, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = gradeSummaryRecycler - presenter.onAttachView(this) - } - - override fun initView() { - gradeSummaryAdapter.setDisplayHeadersAtStartUp(true) - - gradeSummaryRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = gradeSummaryAdapter - } - gradeSummarySwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun updateData(data: List, header: GradeSummaryScrollableHeader) { - gradeSummaryAdapter.apply { - updateDataSet(data, true) - removeAllScrollableHeaders() - addScrollableHeader(header) - } - } - - override fun clearView() { - gradeSummaryAdapter.clear() - } - - override fun resetView() { - gradeSummaryRecycler.scrollToPosition(0) - } - - override fun showContent(show: Boolean) { - gradeSummaryRecycler.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showEmpty(show: Boolean) { - gradeSummaryEmpty.visibility = if (show) VISIBLE else INVISIBLE - } - - override fun showProgress(show: Boolean) { - gradeSummaryProgress.visibility = if (show) VISIBLE else GONE - } - - override fun enableSwipe(enable: Boolean) { - gradeSummarySwipe.isEnabled = enable - } - - override fun showRefresh(show: Boolean) { - gradeSummarySwipe.isRefreshing = show - } - - override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { - presenter.onParentViewLoadData(semesterId, forceRefresh) - } - - override fun onParentReselected() { - presenter.onParentViewReselected() - } - - override fun onParentChangeSemester() { - presenter.onParentViewChangeSemester() - } - - override fun notifyParentDataLoaded(semesterId: Int) { - (parentFragment as? GradeFragment)?.onChildFragmentLoaded(semesterId) - } - - override fun notifyParentRefresh() { - (parentFragment as? GradeFragment)?.onChildRefresh() - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt deleted file mode 100644 index 5737a832f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.summary - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_grade_summary.* - -class GradeSummaryItem( - private val title: String, - private val average: String, - private val predictedGrade: String, - private val finalGrade: String -) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_grade_summary - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.run { - gradeSummaryItemTitle.text = title - gradeSummaryItemAverage.text = average - gradeSummaryItemPredicted.text = predictedGrade - gradeSummaryItemFinal.text = finalGrade - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as GradeSummaryItem - - if (average != other.average) return false - if (title != other.title) return false - if (predictedGrade != other.predictedGrade) return false - if (finalGrade != other.finalGrade) return false - - return true - } - - override fun hashCode(): Int { - var result = title.hashCode() - result = 31 * result + average.hashCode() - result = 31 * result + predictedGrade.hashCode() - result = 31 * result + finalGrade.hashCode() - return result - } - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt deleted file mode 100644 index bab646326..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ /dev/null @@ -1,122 +0,0 @@ -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.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -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( - private val errorHandler: SessionErrorHandler, - private val gradeSummaryRepository: GradeSummaryRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val averageProvider: GradeAverageProvider, - private val schedulers: SchedulersProvider, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - override fun onAttachView(view: GradeSummaryView) { - super.onAttachView(view) - view.initView() - } - - fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { - Timber.i("Loading grade summary data started") - disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it).map { 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( - title = it.subject, - average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, ""), - predictedGrade = it.predictedGrade, - finalGrade = it.finalGrade - ) - }.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 deleted file mode 100644 index f1c535c71..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryScrollableHeader.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.summary - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.scrollable_header_grade_summary.* - -class GradeSummaryScrollableHeader(private val finalAverage: String, private val calculatedAverage: String) - : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.scrollable_header_grade_summary - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder?, - position: Int, payloads: MutableList?) { - holder?.apply { - gradeSummaryScrollableHeaderFinal.text = finalAverage - gradeSummaryScrollableHeaderCalculated.text = calculatedAverage - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as GradeSummaryScrollableHeader - - if (calculatedAverage != other.calculatedAverage) return false - if (finalAverage != other.finalAverage) return false - - return true - } - - override fun hashCode(): Int { - var result = calculatedAverage.hashCode() - result = 31 * result + finalAverage.hashCode() - return result - } - - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt deleted file mode 100644 index 5f7c7b168..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade.summary - -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface GradeSummaryView : BaseSessionView { - - val isViewEmpty: Boolean - - val predictedString: String - - val finalString: String - - fun initView() - - fun updateData(data: List, header: GradeSummaryScrollableHeader) - - fun resetView() - - fun clearView() - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showRefresh(show: Boolean) - - fun showContent(show: Boolean) - - fun showEmpty(show: Boolean) - - fun notifyParentDataLoaded(semesterId: Int) - - fun notifyParentRefresh() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkDialog.kt deleted file mode 100644 index 427841886..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkDialog.kt +++ /dev/null @@ -1,49 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_homework.* - -class HomeworkDialog : DialogFragment() { - - private lateinit var homework: Homework - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(homework: Homework): HomeworkDialog { - return HomeworkDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, homework) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - homework = getSerializable(HomeworkDialog.ARGUMENT_KEY) as Homework - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_homework, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - homeworkDialogDate.text = homework.date.toFormattedString() - homeworkDialogEntryDate.text = homework.entryDate.toFormattedString() - homeworkDialogSubject.text = homework.subject - homeworkDialogTeacher.text = homework.teacher - homeworkDialogContent.text = homework.content - homeworkDialogClose.setOnClickListener { dismiss() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt deleted file mode 100644 index 7325e3ecd..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt +++ /dev/null @@ -1,120 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_homework.* -import javax.inject.Inject - -class HomeworkFragment : BaseSessionFragment(), HomeworkView, MainView.TitledView { - - @Inject - lateinit var presenter: HomeworkPresenter - - @Inject - lateinit var homeworkAdapter: FlexibleAdapter> - - companion object { - private const val SAVED_DATE_KEY = "CURRENT_DATE" - - fun newInstance() = HomeworkFragment() - } - - override val titleStringId: Int - get() = R.string.homework_title - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_homework, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = homeworkRecycler - presenter.onAttachView(this, savedInstanceState?.getLong(HomeworkFragment.SAVED_DATE_KEY)) - } - - override fun initView() { - homeworkAdapter.run { - setOnItemClickListener { presenter.onHomeworkItemSelected(it) } - } - - homeworkRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = homeworkAdapter - addItemDecoration(FlexibleItemDecoration(context) - .withDefaultDivider() - .withDrawDividerOnLastItem(false) - ) - } - homeworkSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - homeworkPreviousButton.setOnClickListener { presenter.onPreviousDay() } - homeworkNextButton.setOnClickListener { presenter.onNextDay() } - } - - override fun updateData(data: List) { - homeworkAdapter.updateDataSet(data, true) - } - - override fun clearData() { - homeworkAdapter.clear() - } - - override fun updateNavigationWeek(date: String) { - homeworkNavDate.text = date - } - - override fun isViewEmpty() = homeworkAdapter.isEmpty - - override fun hideRefresh() { - homeworkSwipe.isRefreshing = false - } - - override fun showEmpty(show: Boolean) { - homeworkEmpty.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showProgress(show: Boolean) { - homeworkProgress.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun enableSwipe(enable: Boolean) { - homeworkSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - homeworkRecycler.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showPreButton(show: Boolean) { - homeworkPreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showNextButton(show: Boolean) { - homeworkNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showTimetableDialog(homework: Homework) { - (activity as? MainActivity)?.showDialogFragment(HomeworkDialog.newInstance(homework)) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putLong(HomeworkFragment.SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkHeader.kt deleted file mode 100644 index 490237883..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkHeader.kt +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractHeaderItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.ExpandableViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.utils.toFormattedString -import io.github.wulkanowy.utils.weekDayName -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.header_homework.* -import org.threeten.bp.LocalDate - -class HomeworkHeader(private val date: LocalDate) : AbstractHeaderItem() { - - override fun getLayoutRes() = R.layout.header_homework - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder( - adapter: FlexibleAdapter>?, holder: HomeworkHeader.ViewHolder, - position: Int, payloads: MutableList? - ) { - holder.run { - homeworkHeaderDay.text = date.weekDayName.capitalize() - homeworkHeaderDate.text = date.toFormattedString() - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as HomeworkHeader - - if (date != other.date) return false - - return true - } - - override fun hashCode(): Int { - return date.hashCode() - } - - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : ExpandableViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt deleted file mode 100644 index 2de9233f2..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractSectionableItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Homework -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_homework.* - -class HomeworkItem(header: HomeworkHeader, val homework: Homework) : - AbstractSectionableItem(header) { - - override fun getLayoutRes() = R.layout.item_homework - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - homeworkItemSubject.text = homework.subject - homeworkItemTeacher.text = homework.teacher - homeworkItemContent.text = homework.content - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as HomeworkItem - - if (homework != other.homework) return false - return true - } - - override fun hashCode(): Int { - var result = homework.hashCode() - result = 31 * result + homework.id.toInt() - return result - } - - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt deleted file mode 100644 index de85d8d11..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt +++ /dev/null @@ -1,123 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.repositories.homework.HomeworkRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.friday -import io.github.wulkanowy.utils.isHolidays -import io.github.wulkanowy.utils.monday -import io.github.wulkanowy.utils.nextOrSameSchoolDay -import io.github.wulkanowy.utils.toFormattedString -import org.threeten.bp.LocalDate -import timber.log.Timber -import java.util.concurrent.TimeUnit -import javax.inject.Inject - -class HomeworkPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val homeworkRepository: HomeworkRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - lateinit var currentDate: LocalDate - private set - - fun onAttachView(view: HomeworkView, date: Long?) { - super.onAttachView(view) - 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, START_DATE to currentDate.toFormattedString("yyyy-MM-dd")) - }) { - Timber.i("Loading homework result: An exception occurred") - view?.run { showEmpty(isViewEmpty()) } - errorHandler.dispatch(it) - }) - } - } - - private fun createHomeworkItem(items: Map>): List { - return items.flatMap { - HomeworkHeader(it.key).let { header -> - it.value.reversed().map { item -> HomeworkItem(header, item) } - } - } - } - - private fun reloadView() { - Timber.i("Reload homework view with the date ${currentDate.toFormattedString()}") - view?.apply { - showProgress(true) - enableSwipe(false) - showContent(false) - showEmpty(false) - clearData() - showNextButton(!currentDate.plusDays(7).isHolidays) - showPreButton(!currentDate.minusDays(7).isHolidays) - updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " + - currentDate.friday.toFormattedString("dd.MM")) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt deleted file mode 100644 index 546d0526f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface HomeworkView : BaseSessionView { - - fun initView() - - fun updateData(data: List) - - fun clearData() - - fun updateNavigationWeek(date: String) - - fun isViewEmpty(): Boolean - - fun hideRefresh() - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun showPreButton(show: Boolean) - - fun showNextButton(show: Boolean) - - fun showTimetableDialog(homework: Homework) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt deleted file mode 100644 index da054bb52..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt +++ /dev/null @@ -1,89 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BaseActivity -import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment -import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment -import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment -import io.github.wulkanowy.utils.setOnSelectPageListener -import kotlinx.android.synthetic.main.activity_login.* -import javax.inject.Inject - -class LoginActivity : BaseActivity(), LoginView { - - @Inject - lateinit var presenter: LoginPresenter - - @Inject - lateinit var loginAdapter: BaseFragmentPagerAdapter - - companion object { - fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java) - } - - override val currentViewIndex: Int - get() = loginViewpager.currentItem - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_login) - messageContainer = loginContainer - - presenter.onAttachView(this) - } - - override fun initAdapter() { - loginAdapter.apply { - containerId = loginViewpager.id - addFragments(listOf( - LoginFormFragment.newInstance(), - LoginSymbolFragment.newInstance(), - LoginStudentSelectFragment.newInstance() - )) - } - - loginViewpager.run { - offscreenPageLimit = 2 - adapter = loginAdapter - setOnSelectPageListener { presenter.onViewSelected(it) } - } - } - - override fun switchView(index: Int) { - loginViewpager.setCurrentItem(index, false) - } - - override fun showActionBar(show: Boolean) { - supportActionBar?.apply { if (show) show() else hide() } - } - - override fun onBackPressed() { - presenter.onBackPressed { super.onBackPressed() } - } - - override fun notifyInitSymbolFragment(loginData: Triple) { - (loginAdapter.getFragmentInstance(1) as? LoginSymbolFragment)?.onParentInitSymbolFragment(loginData) - } - - override fun notifyInitStudentSelectFragment(students: List) { - (loginAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment)?.onParentInitStudentSelectFragment(students) - } - - fun onFormFragmentAccountLogged(students: List, loginData: Triple) { - presenter.onFormViewAccountLogged(students, loginData) - } - - fun onSymbolFragmentAccountLogged(students: List) { - presenter.onSymbolViewAccountLogged(students) - } - - public override fun onDestroy() { - presenter.onDetachView() - super.onDestroy() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt deleted file mode 100644 index a70ff2d65..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import android.content.res.Resources -import android.database.sqlite.SQLiteConstraintException -import com.readystatesoftware.chuck.api.ChuckCollector -import io.github.wulkanowy.R -import io.github.wulkanowy.api.login.BadCredentialsException -import io.github.wulkanowy.ui.base.ErrorHandler -import javax.inject.Inject - -class LoginErrorHandler @Inject constructor( - resources: Resources, - chuckCollector: ChuckCollector -) : ErrorHandler(resources, chuckCollector) { - - var onBadCredentials: () -> Unit = {} - - var onStudentDuplicate: (String) -> Unit = {} - - override fun proceed(error: Throwable) { - when (error) { - is BadCredentialsException -> onBadCredentials() - is SQLiteConstraintException -> onStudentDuplicate(resources.getString(R.string.login_duplicate_student)) - else -> super.proceed(error) - } - } - - override fun clear() { - super.clear() - onBadCredentials = {} - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt deleted file mode 100644 index 7c3e7bace..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import dagger.Module -import dagger.Provides -import dagger.android.ContributesAndroidInjector -import io.github.wulkanowy.di.scopes.PerActivity -import io.github.wulkanowy.di.scopes.PerFragment -import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment -import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment -import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment - -@Module -internal abstract class LoginModule { - - @Module - companion object { - - @JvmStatic - @PerActivity - @Provides - fun provideLoginAdapter(activity: LoginActivity) = BaseFragmentPagerAdapter(activity.supportFragmentManager) - } - - @PerFragment - @ContributesAndroidInjector - abstract fun bindLoginFormFragment(): LoginFormFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindLoginSymbolFragment(): LoginSymbolFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindLoginSelectStudentFragment(): LoginStudentSelectFragment -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt deleted file mode 100644 index 00242a7dc..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import timber.log.Timber -import javax.inject.Inject - -class LoginPresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresenter(errorHandler) { - - override fun onAttachView(view: LoginView) { - super.onAttachView(view) - view.run { - initAdapter() - showActionBar(false) - } - Timber.i("Login view 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 deleted file mode 100644 index 58d356bbd..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BaseView - -interface LoginView : BaseView { - - val currentViewIndex: Int - - fun initAdapter() - - fun switchView(index: Int) - - fun showActionBar(show: Boolean) - - fun notifyInitSymbolFragment(loginData: Triple) - - fun notifyInitStudentSelectFragment(students: List) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt deleted file mode 100644 index b17a1205c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt +++ /dev/null @@ -1,155 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.form - -import android.annotation.SuppressLint -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.view.ViewGroup -import android.view.inputmethod.EditorInfo.IME_ACTION_DONE -import android.view.inputmethod.EditorInfo.IME_NULL -import android.widget.ArrayAdapter -import io.github.wulkanowy.BuildConfig.VERSION_NAME -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.utils.hideSoftInput -import io.github.wulkanowy.utils.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 deleted file mode 100644 index 3ab47c29b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt +++ /dev/null @@ -1,109 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.form - -import com.google.firebase.analytics.FirebaseAnalytics.Param.SUCCESS -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import timber.log.Timber -import javax.inject.Inject -import javax.inject.Named - -class LoginFormPresenter @Inject constructor( - private val schedulers: SchedulersProvider, - private val errorHandler: LoginErrorHandler, - private val studentRepository: StudentRepository, - private val analytics: FirebaseAnalyticsHelper, - @param:Named("isDebug") private val isDebug: Boolean -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: LoginFormView) { - super.onAttachView(view) - view.run { - initView() - if (isDebug) showVersion() else showPrivacyPolicy() - - errorHandler.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) - errorHandler.dispatch(it) - })) - } - - private fun validateCredentials(login: String, password: String): Boolean { - var isCorrect = true - - if (login.isEmpty()) { - view?.setErrorNameRequired() - isCorrect = false - } - - if (password.isEmpty()) { - view?.setErrorPassRequired(focus = isCorrect) - isCorrect = false - } - - if (password.length < 6 && password.isNotEmpty()) { - view?.setErrorPassInvalid(focus = isCorrect) - isCorrect = false - } - return isCorrect - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt deleted file mode 100644 index 80a7b5e9c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.form - -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BaseView - -interface LoginFormView : BaseView { - - fun initView() - - val formNameValue: String - - val formPassValue: String - - val formHostValue: String? - - fun setDefaultCredentials(name: String, pass: String) - - fun setErrorNameRequired() - - fun setErrorPassRequired(focus: Boolean) - - fun setErrorPassInvalid(focus: Boolean) - - fun setErrorPassIncorrect() - - fun clearNameError() - - fun clearPassError() - - fun showSoftKeyboard() - - fun hideSoftKeyboard() - - fun showProgress(show: Boolean) - - fun showContent(show: Boolean) - - fun showVersion() - - fun 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 deleted file mode 100644 index 5c48cf2fb..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt +++ /dev/null @@ -1,87 +0,0 @@ -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 deleted file mode 100644 index 27723c538..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt +++ /dev/null @@ -1,59 +0,0 @@ -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 deleted file mode 100644 index 21dde7b86..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt +++ /dev/null @@ -1,94 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.studentselect - -import com.google.firebase.analytics.FirebaseAnalytics.Param.SUCCESS -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import timber.log.Timber -import java.io.Serializable -import javax.inject.Inject - -class LoginStudentSelectPresenter @Inject constructor( - private val errorHandler: LoginErrorHandler, - private val studentRepository: StudentRepository, - private val schedulers: SchedulersProvider, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - var students = emptyList() - - var selectedStudents = mutableListOf() - - fun onAttachView(view: LoginStudentSelectView, students: Serializable?) { - super.onAttachView(view) - view.run { - initView() - enableSignIn(false) - errorHandler.onStudentDuplicate = { - showMessage(it) - Timber.i("The student already registered in the app was selected") - } - } - - if (students is List<*> && students.isNotEmpty()) { - loadData(students.filterIsInstance()) - } - } - - fun 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) } - Timber.i("Registration result: An exception occurred ") - errorHandler.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 deleted file mode 100644 index 3967313c3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.studentselect - -import io.github.wulkanowy.ui.base.BaseView - -interface LoginStudentSelectView : BaseView { - - fun initView() - - fun updateData(data: List) - - fun openMainView() - - fun showProgress(show: Boolean) - - fun showContent(show: Boolean) - - fun 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 deleted file mode 100644 index 87de0a818..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt +++ /dev/null @@ -1,116 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.symbol - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.view.ViewGroup -import android.view.inputmethod.EditorInfo.IME_ACTION_DONE -import android.view.inputmethod.EditorInfo.IME_NULL -import android.widget.ArrayAdapter -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.utils.hideSoftInput -import io.github.wulkanowy.utils.setOnTextChangedListener -import io.github.wulkanowy.utils.showSoftInput -import kotlinx.android.synthetic.main.fragment_login_symbol.* -import javax.inject.Inject - -class LoginSymbolFragment : BaseFragment(), LoginSymbolView { - - @Inject - lateinit var presenter: LoginSymbolPresenter - - companion object { - private const val SAVED_LOGIN_DATA = "LOGIN_DATA" - - fun newInstance() = LoginSymbolFragment() - } - - override val symbolNameError: CharSequence? - get() = loginSymbolNameLayout.error - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_login_symbol, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this, savedInstanceState?.getSerializable(SAVED_LOGIN_DATA)) - } - - override fun initView() { - loginSymbolSignIn.setOnClickListener { presenter.attemptLogin(loginSymbolName.text.toString()) } - - loginSymbolName.setOnTextChangedListener { presenter.onSymbolTextChanged() } - - loginSymbolName.apply { - setOnEditorActionListener { _, id, _ -> - if (id == IME_ACTION_DONE || id == IME_NULL) loginSymbolSignIn.callOnClick() else false - } - setAdapter(ArrayAdapter(context, android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values))) - } - } - - fun onParentInitSymbolFragment(loginData: Triple) { - presenter.onParentInitSymbolView(loginData) - } - - override fun setErrorSymbolIncorrect() { - loginSymbolNameLayout.apply { - requestFocus() - error = getString(R.string.login_incorrect_symbol) - } - } - - override fun setErrorSymbolRequire() { - loginSymbolNameLayout.apply { - requestFocus() - error = getString(R.string.login_field_required) - } - } - - override fun clearSymbolError() { - loginSymbolNameLayout.error = null - } - - override fun clearAndFocusSymbol() { - loginSymbolNameLayout.apply { - editText?.text = null - requestFocus() - } - } - - override fun showSoftKeyboard() { - activity?.showSoftInput() - } - - override fun hideSoftKeyboard() { - activity?.hideSoftInput() - } - - override fun showProgress(show: Boolean) { - loginSymbolProgress.visibility = if (show) VISIBLE else GONE - } - - override fun showContent(show: Boolean) { - loginSymbolContainer.visibility = if (show) VISIBLE else GONE - } - - override fun notifyParentAccountLogged(students: List) { - (activity as? LoginActivity)?.onSymbolFragmentAccountLogged(students) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putSerializable(SAVED_LOGIN_DATA, presenter.loginData) - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt deleted file mode 100644 index 0adb9e16f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt +++ /dev/null @@ -1,86 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.symbol - -import com.google.firebase.analytics.FirebaseAnalytics.Param.SUCCESS -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.reactivex.Single -import timber.log.Timber -import java.io.Serializable -import javax.inject.Inject - -class LoginSymbolPresenter @Inject constructor( - private val studentRepository: StudentRepository, - private val errorHandler: LoginErrorHandler, - private val schedulers: SchedulersProvider, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - var loginData: Triple? = null - - @Suppress("UNCHECKED_CAST") - fun onAttachView(view: LoginSymbolView, savedLoginData: Serializable?) { - super.onAttachView(view) - view.initView() - if (savedLoginData is Triple<*, *, *>) { - loginData = savedLoginData as Triple - } - } - - fun onSymbolTextChanged() { - view?.apply { if (symbolNameError != null) clearSymbolError() } - } - - fun attemptLogin(symbol: String) { - if (symbol.isBlank()) { - view?.setErrorSymbolRequire() - return - } - - disposable.add( - Single.fromCallable { if (loginData == null) throw IllegalArgumentException("Login data is null") else loginData } - .flatMap { studentRepository.getStudents(it.first, it.second, it.third, symbol) } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doOnSubscribe { - view?.apply { - hideSoftKeyboard() - showProgress(true) - showContent(false) - } - Timber.i("Login with symbol started") - } - .doFinally { - view?.apply { - showProgress(false) - showContent(true) - } - } - .subscribe({ - analytics.logEvent("registration_symbol", SUCCESS to true, "students" to it.size, "endpoint" to loginData?.third, "symbol" to symbol, "error" to "No error") - view?.apply { - if (it.isEmpty()) { - Timber.i("Login with symbol result: Empty student list") - setErrorSymbolIncorrect() - } else { - Timber.i("Login with symbol result: Success") - notifyParentAccountLogged(it) - } - } - }, { - Timber.i("Login with symbol result: An exception occurred") - analytics.logEvent("registration_symbol", SUCCESS to false, "students" to -1, "endpoint" to loginData?.third, "symbol" to symbol, "error" to it.localizedMessage) - errorHandler.dispatch(it) - })) - } - - fun onParentInitSymbolView(loginData: Triple) { - this.loginData = loginData - view?.apply { - clearAndFocusSymbol() - showSoftKeyboard() - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt deleted file mode 100644 index 2e5143ef1..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.symbol - -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.ui.base.BaseView - -interface LoginSymbolView : BaseView { - - val symbolNameError: CharSequence? - - fun initView() - - fun setErrorSymbolIncorrect() - - fun setErrorSymbolRequire() - - fun clearSymbolError() - - fun clearAndFocusSymbol() - - fun showSoftKeyboard() - - fun hideSoftKeyboard() - - fun showProgress(show: Boolean) - - fun showContent(show: Boolean) - - fun notifyParentAccountLogged(students: List) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt deleted file mode 100644 index cff6175c0..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt +++ /dev/null @@ -1,71 +0,0 @@ -package io.github.wulkanowy.ui.modules.luckynumber - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainView -import kotlinx.android.synthetic.main.fragment_lucky_number.* -import javax.inject.Inject - -class LuckyNumberFragment : BaseSessionFragment(), LuckyNumberView, MainView.TitledView { - - @Inject - lateinit var presenter: LuckyNumberPresenter - - companion object { - fun newInstance() = LuckyNumberFragment() - } - - override val titleStringId: Int - get() = R.string.lucky_number_title - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_lucky_number, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this) - } - - override fun initView() { - luckyNumberSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun updateData(data: LuckyNumber) { - luckyNumberText.text = data.luckyNumber.toString() - } - - override fun hideRefresh() { - luckyNumberSwipe.isRefreshing = false - } - - override fun showEmpty(show: Boolean) { - luckyNumberEmpty.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showProgress(show: Boolean) { - luckyNumberProgress.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun enableSwipe(enable: Boolean) { - luckyNumberSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - luckyNumberContent.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun isViewEmpty(): Boolean { - return luckyNumberText.text.isBlank() - } - - override fun onDestroyView() { - super.onDestroyView() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt deleted file mode 100644 index 8ef638db7..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt +++ /dev/null @@ -1,76 +0,0 @@ -package io.github.wulkanowy.ui.modules.luckynumber - -import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import timber.log.Timber -import javax.inject.Inject - -class LuckyNumberPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val luckyNumberRepository: LuckyNumberRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: LuckyNumberView) { - super.onAttachView(view) - 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 deleted file mode 100644 index 5c19142c5..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.ui.modules.luckynumber - -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface LuckyNumberView : BaseSessionView { - - fun initView() - - fun updateData(data: LuckyNumber) - - fun hideRefresh() - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun isViewEmpty(): Boolean -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt deleted file mode 100644 index 7e3eeb56b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt +++ /dev/null @@ -1,77 +0,0 @@ -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 - 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)) - } - - override fun onDestroy() { - super.onDestroy() - presenter.onDetachView() - } -} 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 deleted file mode 100644 index bba0974b6..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureItem.kt +++ /dev/null @@ -1,54 +0,0 @@ -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 deleted file mode 100644 index 4fd81e960..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt +++ /dev/null @@ -1,62 +0,0 @@ -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( - private val errorHandler: ErrorHandler, - private val schedulers: SchedulersProvider, - private val studentRepository: StudentRepository, - private val sharedPref: SharedPrefHelper -) : BasePresenter(errorHandler) { - - 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 deleted file mode 100644 index 49c3f1dcd..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt +++ /dev/null @@ -1,19 +0,0 @@ -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 deleted file mode 100644 index 66141c913..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt +++ /dev/null @@ -1,183 +0,0 @@ -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 deleted file mode 100644 index 5ca41b711..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt +++ /dev/null @@ -1,201 +0,0 @@ -package io.github.wulkanowy.ui.modules.main - -import android.content.Context -import android.content.Intent -import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK -import android.content.Intent.FLAG_ACTIVITY_NEW_TASK -import android.os.Bundle -import android.view.Menu -import android.view.MenuItem -import androidx.appcompat.app.AlertDialog -import androidx.core.content.ContextCompat -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.Fragment -import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.TitleState.ALWAYS_SHOW -import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem -import com.ncapdevi.fragnav.FragNavController -import com.ncapdevi.fragnav.FragNavController.Companion.HIDE -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.BaseActivity -import io.github.wulkanowy.ui.modules.account.AccountDialog -import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment -import io.github.wulkanowy.ui.modules.exam.ExamFragment -import io.github.wulkanowy.ui.modules.grade.GradeFragment -import io.github.wulkanowy.ui.modules.homework.HomeworkFragment -import io.github.wulkanowy.ui.modules.login.LoginActivity -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 - 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()) - } - - fun showExpiredDialog() { - AlertDialog.Builder(this) - .setTitle(R.string.main_session_expired) - .setMessage(R.string.main_session_relogin) - .setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onLoginSelected() } - .setNegativeButton(android.R.string.cancel) { _, _ -> } - .show() - } - - override fun notifyMenuViewReselected() { - (navController.currentStack?.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 openLoginView() { - startActivity(LoginActivity.getStartIntent(this) - .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) }) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - navController.onSaveInstanceState(outState) - intent.removeExtra(EXTRA_START_MENU) - } - - override fun onDestroy() { - super.onDestroy() - presenter.onDetachView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt deleted file mode 100644 index de4405b74..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt +++ /dev/null @@ -1,100 +0,0 @@ -package io.github.wulkanowy.ui.modules.main - -import com.ncapdevi.fragnav.FragNavController -import dagger.Module -import dagger.Provides -import dagger.android.ContributesAndroidInjector -import io.github.wulkanowy.R -import io.github.wulkanowy.di.scopes.PerFragment -import io.github.wulkanowy.ui.modules.about.AboutFragment -import io.github.wulkanowy.ui.modules.about.AboutModule -import io.github.wulkanowy.ui.modules.account.AccountDialog -import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment -import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment -import io.github.wulkanowy.ui.modules.exam.ExamFragment -import io.github.wulkanowy.ui.modules.grade.GradeFragment -import io.github.wulkanowy.ui.modules.grade.GradeModule -import io.github.wulkanowy.ui.modules.homework.HomeworkFragment -import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment -import io.github.wulkanowy.ui.modules.message.MessageFragment -import io.github.wulkanowy.ui.modules.message.MessageModule -import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment -import io.github.wulkanowy.ui.modules.more.MoreFragment -import io.github.wulkanowy.ui.modules.note.NoteFragment -import io.github.wulkanowy.ui.modules.settings.SettingsFragment -import io.github.wulkanowy.ui.modules.timetable.TimetableFragment -import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment - -@Module -abstract class MainModule { - - @Module - companion object { - - @JvmStatic - @Provides - fun provideFragNavController(activity: MainActivity): FragNavController { - return FragNavController(activity.supportFragmentManager, R.id.mainFragmentContainer) - } - } - - @PerFragment - @ContributesAndroidInjector - abstract fun bindAttendanceFragment(): AttendanceFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindAttendanceSummaryFragment(): AttendanceSummaryFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindExamFragment(): ExamFragment - - @PerFragment - @ContributesAndroidInjector(modules = [GradeModule::class]) - abstract fun bindGradeFragment(): GradeFragment - - @PerFragment - @ContributesAndroidInjector(modules = [MessageModule::class]) - abstract fun bindMessagesFragment(): MessageFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindMessagePreviewFragment(): MessagePreviewFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindMoreFragment(): MoreFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindTimetableFragment(): TimetableFragment - - @PerFragment - @ContributesAndroidInjector(modules = [AboutModule::class]) - abstract fun bindAboutFragment(): AboutFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindSettingsFragment(): SettingsFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindNoteFragment(): NoteFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindHomeworkFragment(): HomeworkFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindLuckyNumberFragment(): LuckyNumberFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindCompletedLessonsFragment(): CompletedLessonsFragment - - @PerFragment - @ContributesAndroidInjector - abstract fun bindAccountDialog(): AccountDialog -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt deleted file mode 100644 index 12f2bb048..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt +++ /dev/null @@ -1,112 +0,0 @@ -package io.github.wulkanowy.ui.modules.main - -import com.google.firebase.analytics.FirebaseAnalytics.Event.APP_OPEN -import com.google.firebase.analytics.FirebaseAnalytics.Param.DESTINATION -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.services.sync.SyncManager -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.reactivex.Completable -import timber.log.Timber -import javax.inject.Inject - -class MainPresenter @Inject constructor( - private val errorHandler: ErrorHandler, - private val studentRepository: StudentRepository, - private val prefRepository: PreferencesRepository, - private val syncManager: SyncManager, - private val schedulers: SchedulersProvider, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - fun onAttachView(view: MainView, 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 - } - - fun onLoginSelected() { - Timber.i("Attempt to switch the student after the session expires") - disposable.add(studentRepository.getCurrentStudent(false) - .flatMapCompletable { studentRepository.logoutStudent(it) } - .andThen(studentRepository.getSavedStudents(false)) - .flatMapCompletable { - if (it.isNotEmpty()) { - Timber.i("Switching current student") - studentRepository.switchStudent(it[0]) - } else Completable.complete() - } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ - Timber.i("Switch student result: Open login view") - view?.openLoginView() - }, { - Timber.i("Switch student result: An exception occurred") - errorHandler.dispatch(it) - })) - } - - 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 deleted file mode 100644 index a786a3c33..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt +++ /dev/null @@ -1,53 +0,0 @@ -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() - - fun openLoginView() - - 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 deleted file mode 100644 index 7bd35f2d2..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt +++ /dev/null @@ -1,103 +0,0 @@ -package io.github.wulkanowy.ui.modules.message - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import io.github.wulkanowy.R -import io.github.wulkanowy.data.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 deleted file mode 100644 index 26568e22f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt +++ /dev/null @@ -1,62 +0,0 @@ -package io.github.wulkanowy.ui.modules.message - -import android.graphics.Typeface.BOLD -import android.graphics.Typeface.NORMAL -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_message.* - -class MessageItem(val message: Message, private val noSubjectString: String) : - AbstractFlexibleItem() { - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun getLayoutRes() = R.layout.item_message - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - val style = if (message.unread) BOLD else NORMAL - - messageItemAuthor.run { - text = if (message.recipient.isNotBlank()) message.recipient else message.sender - setTypeface(null, style) - } - messageItemSubject.run { - text = if (message.subject.isNotBlank()) message.subject else noSubjectString - setTypeface(null, style) - } - messageItemDate.run { - text = message.date.toFormattedString() - setTypeface(null, style) - } - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as MessageItem - - if (message != other.message) return false - return true - } - - override fun hashCode(): Int { - return message.hashCode() - } - - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageModule.kt deleted file mode 100644 index e4c202d9f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageModule.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.ui.modules.message - -import dagger.Module -import dagger.Provides -import dagger.android.ContributesAndroidInjector -import io.github.wulkanowy.di.scopes.PerChildFragment -import io.github.wulkanowy.di.scopes.PerFragment -import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment - -@Module -abstract class MessageModule { - - @Module - companion object { - - @JvmStatic - @PerFragment - @Provides - fun provideMessageAdapter(fragment: MessageFragment) = BaseFragmentPagerAdapter(fragment.childFragmentManager) - } - - @PerChildFragment - @ContributesAndroidInjector - abstract fun bindMessageTabFragment(): MessageTabFragment -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt deleted file mode 100644 index 888df0400..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.wulkanowy.ui.modules.message - -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.SchedulersProvider -import io.reactivex.Completable -import timber.log.Timber -import java.util.concurrent.TimeUnit.MILLISECONDS -import javax.inject.Inject - -class MessagePresenter @Inject constructor( - errorHandler: ErrorHandler, - private val schedulers: SchedulersProvider -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: MessageView) { - super.onAttachView(view) - 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 deleted file mode 100644 index 2aa4d78ec..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.ui.modules.message - -import io.github.wulkanowy.ui.base.BaseView - -interface MessageView : BaseView { - - val currentPageIndex: Int - - fun initView() - - fun showContent(show: Boolean) - - fun showProgress(show: Boolean) - - fun notifyChildLoadData(index: Int, forceRefresh: Boolean) - - fun 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 deleted file mode 100644 index 057d81dc6..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ /dev/null @@ -1,157 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.preview - -import android.annotation.SuppressLint -import android.os.Bundle -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.view.ViewGroup -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.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 - -@SuppressLint("SetTextI18n") -class MessagePreviewFragment : BaseSessionFragment(), 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 - } - - override fun setRecipient(recipient: String) { - messagePreviewAuthor.text = "${getString(R.string.message_to)} $recipient" - } - - override fun setSender(sender: String) { - messagePreviewAuthor.text = "${getString(R.string.message_from)} $sender" - } - - override fun setDate(date: String) { - messagePreviewDate.text = getString(R.string.message_date, date) - } - - override fun setContent(content: String) { - messagePreviewContent.text = content - } - - override fun showProgress(show: Boolean) { - messagePreviewProgress.visibility = if (show) VISIBLE else GONE - } - - override fun 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 deleted file mode 100644 index 31641d754..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ /dev/null @@ -1,131 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.preview - -import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.repositories.message.MessageRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.toFormattedString -import timber.log.Timber -import javax.inject.Inject - -class MessagePreviewPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val messageRepository: MessageRepository, - private val studentRepository: StudentRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - var messageId = 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", START_DATE to message.date.toFormattedString("yyyy.MM.dd"), "length" to message.content?.length) - }) { - Timber.i("Loading message $id preview result: An exception occurred ") - view?.showMessageError() - errorHandler.dispatch(it) - }) - } - } - - fun onReply(): Boolean { - return if (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 deleted file mode 100644 index 8bc528a3d..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.preview - -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface MessagePreviewView : BaseSessionView { - - val noSubjectString: String - - 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 deleted file mode 100644 index be433b008..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChip.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.send - -import android.graphics.drawable.Drawable -import android.net.Uri -import com.pchmn.materialchips.model.ChipInterface -import io.github.wulkanowy.data.db.entities.Recipient - -class RecipientChip(var recipient: Recipient) : ChipInterface { - - override fun getAvatarDrawable(): Drawable? = null - - override fun getAvatarUri(): Uri? = null - - override fun getId(): Any = recipient.id - - override fun getLabel(): String = recipient.name - - override fun getInfo(): String { - return recipient.realName.run { - substringBeforeLast("-").let { sub -> - when { - (sub == this) -> this - (sub.indexOf('(') != -1) -> indexOf("(").let { substring(if (it != -1) it else 0) } - (sub.indexOf('[') != -1) -> indexOf("[").let { substring(if (it != -1) it else 0) } - else -> substringAfter('-') - } - }.trim() - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt deleted file mode 100644 index 3a44ea86f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt +++ /dev/null @@ -1,134 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.send - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.view.Menu -import android.view.MenuItem -import android.view.View.GONE -import android.view.View.VISIBLE -import android.widget.Toast -import android.widget.Toast.LENGTH_LONG -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.ui.base.BaseActivity -import io.github.wulkanowy.utils.hideSoftInput -import io.github.wulkanowy.utils.showSoftInput -import kotlinx.android.synthetic.main.activity_send_message.* -import javax.inject.Inject - -class SendMessageActivity : BaseActivity(), SendMessageView { - - @Inject - lateinit var presenter: SendMessagePresenter - - companion object { - private const val EXTRA_MESSAGE = "EXTRA_MESSAGE" - 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() - } - - override fun onDestroy() { - presenter.onDetachView() - super.onDestroy() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt deleted file mode 100644 index 8dedaba01..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt +++ /dev/null @@ -1,165 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.send - -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.repositories.message.MessageRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.recipient.RecipientRepository -import io.github.wulkanowy.data.repositories.reportingunit.ReportingUnitRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.toFormattedString -import io.reactivex.Completable -import timber.log.Timber -import javax.inject.Inject - -class SendMessagePresenter @Inject constructor( - private val errorHandler: ErrorHandler, - private val schedulers: SchedulersProvider, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val messageRepository: MessageRepository, - private val reportingUnitRepository: ReportingUnitRepository, - private val recipientRepository: RecipientRepository, - private val preferencesRepository: PreferencesRepository, - private val analytics: FirebaseAnalyticsHelper -) : BasePresenter(errorHandler) { - - fun onAttachView(view: SendMessageView, message: Message?, 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 deleted file mode 100644 index 8a2cb4f61..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.send - -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.ui.base.BaseView - -interface SendMessageView : BaseView { - - val formRecipientsData: List - - val formSubjectValue: String - - val formContentValue: String - - val messageRequiredRecipients: String - - val messageContentMinLength: String - - val messageSuccess: String - - fun setReportingUnit(unit: ReportingUnit) - - fun setRecipients(recipients: List) - - fun setSelectedRecipients(recipients: List) - - fun showProgress(show: Boolean) - - fun showContent(show: Boolean) - - fun showEmpty(show: Boolean) - - fun showActionBar(show: Boolean) - - fun setSubject(subject: String) - - fun setContent(content: String) - - fun showSoftInput(show: Boolean) - - fun popView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt deleted file mode 100644 index 3859853fc..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt +++ /dev/null @@ -1,134 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.tab - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.message.MessageFolder -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.message.MessageFragment -import io.github.wulkanowy.ui.modules.message.MessageItem -import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_message_tab.* -import javax.inject.Inject - -class MessageTabFragment : BaseSessionFragment(), 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 deleted file mode 100644 index 2173c951d..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ /dev/null @@ -1,103 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.tab - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.repositories.message.MessageFolder -import io.github.wulkanowy.data.repositories.message.MessageRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.ui.modules.message.MessageItem -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import timber.log.Timber -import javax.inject.Inject - -class MessageTabPresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val messageRepository: MessageRepository, - private val studentRepository: StudentRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - lateinit var folder: MessageFolder - - fun onAttachView(view: MessageTabView, folder: MessageFolder) { - super.onAttachView(view) - view.initView() - this.folder = folder - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the $folder message") - onParentViewLoadData(true) - } - - fun 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 deleted file mode 100644 index 01a188e75..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.tab - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.ui.base.session.BaseSessionView -import io.github.wulkanowy.ui.modules.message.MessageItem - -interface MessageTabView : BaseSessionView { - - val noSubjectString: String - - val isViewEmpty: Boolean - - fun initView() - - fun updateData(data: List) - - fun updateItem(item: AbstractFlexibleItem<*>) - - fun clearView() - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun showEmpty(show: Boolean) - - fun showRefresh(show: Boolean) - - fun openMessage(messageId: Long) - - fun notifyParentDataLoaded() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt deleted file mode 100644 index 4823dedf3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt +++ /dev/null @@ -1,146 +0,0 @@ -package io.github.wulkanowy.ui.modules.more - -import android.graphics.drawable.Drawable -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.content.ContextCompat -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.about.AboutFragment -import io.github.wulkanowy.ui.modules.homework.HomeworkFragment -import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.ui.modules.message.MessageFragment -import io.github.wulkanowy.ui.modules.note.NoteFragment -import io.github.wulkanowy.ui.modules.settings.SettingsFragment -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_more.* -import javax.inject.Inject - -class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.MainChildView { - - @Inject - lateinit var presenter: MorePresenter - - @Inject - lateinit var moreAdapter: FlexibleAdapter> - - companion object { - fun newInstance() = MoreFragment() - } - - override val titleStringId: Int - get() = R.string.more_title - - - override val messagesRes: Pair? - get() { - return context?.run { - getString(R.string.message_title) to - ContextCompat.getDrawable(this, R.drawable.ic_more_messages_24dp) - } - } - - override val homeworkRes: Pair? - get() { - return context?.run { - getString(R.string.homework_title) to ContextCompat.getDrawable(this, R.drawable.ic_menu_main_homework_24dp) - } - } - - override val noteRes: Pair? - get() { - return context?.run { - getString(R.string.note_title) to ContextCompat.getDrawable(this, R.drawable.ic_menu_main_note_24dp) - } - } - - override val luckyNumberRes: Pair? - get() { - return context?.run { - getString(R.string.lucky_number_title) to - ContextCompat.getDrawable(this, R.drawable.ic_more_lucky_number_24dp) - } - } - - override val settingsRes: Pair? - get() { - return context?.run { - getString(R.string.settings_title) to - ContextCompat.getDrawable(this, R.drawable.ic_more_settings_24dp) - } - } - - override val aboutRes: Pair? - get() { - return context?.run { - getString(R.string.about_title) to - ContextCompat.getDrawable(this, R.drawable.ic_all_about_24dp) - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_more, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this) - } - - override fun initView() { - moreAdapter.run { setOnItemClickListener { presenter.onItemSelected(it) } } - - moreRecycler.apply { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = moreAdapter - } - } - - override fun onFragmentReselected() { - 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 openSettingsView() { - (activity as? MainActivity)?.pushView(SettingsFragment.newInstance()) - } - - override fun openAboutView() { - (activity as? MainActivity)?.pushView(AboutFragment.newInstance()) - } - - override fun popView() { - (activity as? MainActivity)?.popView() - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt deleted file mode 100644 index 85b604e77..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.wulkanowy.ui.modules.more - -import android.graphics.drawable.Drawable -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_more.* - -class MoreItem(val title: String, private val drawable: Drawable?) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_more - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - moreItemTitle.text = title - moreItemImage.setImageDrawable(drawable) - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as MoreItem - - if (title != other.title) return false - - return true - } - - override fun hashCode(): Int { - return title.hashCode() - } - - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt deleted file mode 100644 index a7626ec0d..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.ui.modules.more - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import timber.log.Timber -import javax.inject.Inject - -class MorePresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresenter(errorHandler) { - - override fun onAttachView(view: MoreView) { - super.onAttachView(view) - 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() - settingsRes?.first -> openSettingsView() - aboutRes?.first -> openAboutView() - } - } - } - } - - fun onViewReselected() { - Timber.i("More view is reselected") - view?.popView() - } - - private fun loadData() { - Timber.i("Load items for more view") - view?.run { - updateData(listOfNotNull( - messagesRes?.let { MoreItem(it.first, it.second) }, - homeworkRes?.let { MoreItem(it.first, it.second) }, - noteRes?.let { MoreItem(it.first, it.second) }, - luckyNumberRes?.let { MoreItem(it.first, it.second) }, - settingsRes?.let { MoreItem(it.first, it.second) }, - aboutRes?.let { MoreItem(it.first, it.second) }) - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt deleted file mode 100644 index 333edc5df..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.ui.modules.more - -import android.graphics.drawable.Drawable -import io.github.wulkanowy.ui.base.BaseView - -interface MoreView : BaseView { - - val messagesRes: Pair? - - val homeworkRes: Pair? - - val noteRes: Pair? - - val luckyNumberRes: Pair? - - val settingsRes: Pair? - - val aboutRes: Pair? - - fun initView() - - fun updateData(data: List) - - fun openSettingsView() - - fun openAboutView() - - fun popView() - - fun openMessagesView() - - fun openHomeworkView() - - fun openNoteView() - - fun openLuckyNumberView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt deleted file mode 100644 index 492aeab26..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.ui.modules.note - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_note.* - -class NoteDialog : DialogFragment() { - - private lateinit var note: Note - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(exam: Note): NoteDialog { - return NoteDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - note = getSerializable(ARGUMENT_KEY) as Note - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_note, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - noteDialogDate.text = note.date.toFormattedString() - noteDialogCategory.text = note.category - noteDialogTeacher.text = note.teacher - noteDialogContent.text = note.content - noteDialogClose.setOnClickListener { dismiss() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt deleted file mode 100644 index fc773e10b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt +++ /dev/null @@ -1,105 +0,0 @@ -package io.github.wulkanowy.ui.modules.note - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_note.* -import javax.inject.Inject - -class NoteFragment : BaseSessionFragment(), NoteView, MainView.TitledView { - - @Inject - lateinit var presenter: NotePresenter - - @Inject - lateinit var noteAdapter: FlexibleAdapter> - - companion object { - fun newInstance() = NoteFragment() - } - - override val titleStringId: Int - get() = R.string.note_title - - override val isViewEmpty: Boolean - get() = noteAdapter.isEmpty - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_note, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - presenter.onAttachView(this) - } - - override fun initView() { - noteAdapter.run { - setOnItemClickListener { presenter.onNoteItemSelected(it) } - } - - noteRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = noteAdapter - addItemDecoration(FlexibleItemDecoration(context) - .withDefaultDivider() - .withDrawDividerOnLastItem(false) - ) - } - noteSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - } - - override fun showNoteDialog(note: Note) { - (activity as? MainActivity)?.showDialogFragment(NoteDialog.newInstance(note)) - } - - override fun updateData(data: List) { - noteAdapter.updateDataSet(data, true) - } - - override fun updateItem(item: AbstractFlexibleItem<*>) { - noteAdapter.updateItem(item) - } - - override fun clearData() { - noteAdapter.clear() - } - - override fun showEmpty(show: Boolean) { - noteEmpty.visibility = if (show) VISIBLE else GONE - } - - override fun showProgress(show: Boolean) { - noteProgress.visibility = if (show) VISIBLE else GONE - } - - override fun enableSwipe(enable: Boolean) { - noteSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - noteRecycler.visibility = if (show) VISIBLE else GONE - } - - override fun hideRefresh() { - noteSwipe.isRefreshing = false - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt deleted file mode 100644 index dabeef74a..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.ui.modules.note - -import android.graphics.Typeface.BOLD -import android.graphics.Typeface.NORMAL -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_note.* - -class NoteItem(val note: Note) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_note - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): NoteItem.ViewHolder { - return NoteItem.ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: NoteItem.ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - noteItemDate.apply { - text = note.date.toFormattedString() - setTypeface(null, if (note.isRead) NORMAL else BOLD) - } - noteItemType.apply { - text = note.category - setTypeface(null, if (note.isRead) NORMAL else BOLD) - } - noteItemTeacher.text = note.teacher - noteItemContent.text = note.content - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as NoteItem - - if (note != other.note) return false - if (note.id != other.note.id) return false - return true - } - - override fun hashCode(): Int { - var result = note.hashCode() - result = 31 * result + note.id.toInt() - return result - } - - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt deleted file mode 100644 index 136a44135..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt +++ /dev/null @@ -1,92 +0,0 @@ -package io.github.wulkanowy.ui.modules.note - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.repositories.note.NoteRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import timber.log.Timber -import javax.inject.Inject - -class NotePresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val studentRepository: StudentRepository, - private val noteRepository: NoteRepository, - private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - override fun onAttachView(view: NoteView) { - super.onAttachView(view) - 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 deleted file mode 100644 index 38e6c94fd..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.ui.modules.note - -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface NoteView : BaseSessionView { - - val isViewEmpty: Boolean - - fun initView() - - fun updateData(data: List) - - fun updateItem(item: AbstractFlexibleItem<*>) - - fun clearData() - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun hideRefresh() - - fun showNoteDialog(note: Note) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt deleted file mode 100644 index 2baad0eb7..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt +++ /dev/null @@ -1,74 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings - -import android.content.Context -import android.content.SharedPreferences -import android.os.Bundle -import 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 onResume() { - super.onResume() - preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this) - } - - override fun onPause() { - super.onPause() - preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt deleted file mode 100644 index c25198d56..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings - -import com.readystatesoftware.chuck.api.ChuckCollector -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.services.sync.SyncManager -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.isHolidays -import org.threeten.bp.LocalDate.now -import timber.log.Timber -import javax.inject.Inject - -class SettingsPresenter @Inject constructor( - errorHandler: ErrorHandler, - private val preferencesRepository: PreferencesRepository, - private val analytics: FirebaseAnalyticsHelper, - private val syncManager: SyncManager, - private val chuckCollector: ChuckCollector -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: SettingsView) { - super.onAttachView(view) - Timber.i("Settings view 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 deleted file mode 100644 index 1c4bf3b0b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings - -import io.github.wulkanowy.ui.base.BaseView - -interface SettingsView : BaseView { - - fun 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 deleted file mode 100644 index 2bd625332..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.wulkanowy.ui.modules.splash - -import android.os.Bundle -import android.widget.Toast -import android.widget.Toast.LENGTH_LONG -import io.github.wulkanowy.ui.base.BaseActivity -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.main.MainActivity -import javax.inject.Inject - -class SplashActivity : BaseActivity(), SplashView { - - @Inject - lateinit var presenter: SplashPresenter - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - presenter.onAttachView(this) - } - - override fun openLoginView() { - startActivity(LoginActivity.getStartIntent(this)) - finish() - } - - override fun openMainView() { - startActivity(MainActivity.getStartIntent(this)) - finish() - } - - override fun showError(text: String, error: Throwable) { - Toast.makeText(this, text, LENGTH_LONG).show() - } - - override fun onDestroy() { - presenter.onDetachView() - super.onDestroy() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt deleted file mode 100644 index e6a9d7b3e..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.ui.modules.splash - -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.SchedulersProvider -import javax.inject.Inject - -class SplashPresenter @Inject constructor( - private val studentRepository: StudentRepository, - private val errorHandler: ErrorHandler, - private val schedulers: SchedulersProvider -) : BasePresenter(errorHandler) { - - override fun onAttachView(view: SplashView) { - super.onAttachView(view) - disposable.add(studentRepository.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 deleted file mode 100644 index 9efd8123b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.ui.modules.splash - -import io.github.wulkanowy.ui.base.BaseView - -interface SplashView : BaseView { - - fun openLoginView() - - fun openMainView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt deleted file mode 100644 index 7e286c923..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt +++ /dev/null @@ -1,150 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -import android.annotation.SuppressLint -import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_timetable.* -import org.threeten.bp.LocalDateTime - -class TimetableDialog : DialogFragment() { - - private lateinit var lesson: Timetable - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(exam: Timetable): TimetableDialog { - return TimetableDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - lesson = getSerializable(ARGUMENT_KEY) as Timetable - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_timetable, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - lesson.run { - setInfo(info, teacher, canceled, changes) - setSubject(subject, subjectOld) - setTeacher(teacher, teacherOld) - setGroup(group) - setRoom(room, roomOld) - setTime(start, end) - } - - timetableDialogClose.setOnClickListener { dismiss() } - } - - private fun setSubject(subject: String, subjectOld: String) { - timetableDialogSubject.text = subject - if (subjectOld.isNotBlank() && subjectOld != subject) { - timetableDialogSubject.run { - paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG - text = subjectOld - } - timetableDialogSubjectNew.run { - visibility = VISIBLE - text = subject - } - } - } - - private fun setInfo(info: String, teacher: String, canceled: Boolean, changes: Boolean) { - when { - info.isNotBlank() -> timetableDialogChanges.text = when { - canceled && !changes -> "Lekcja odwołana: $info" - changes && teacher.isNotBlank() -> "Zastępstwo: $teacher" - changes && teacher.isBlank() -> "Zastępstwo, ${info.decapitalize()}" - else -> info.capitalize() - } - else -> { - timetableDialogChangesTitle.visibility = GONE - timetableDialogChanges.visibility = GONE - } - } - } - - private fun setTeacher(teacher: String, teacherOld: String) { - when { - teacherOld.isNotBlank() && teacherOld != teacher -> { - timetableDialogTeacher.run { - visibility = VISIBLE - paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG - text = teacherOld - } - if (teacher.isNotBlank()) { - timetableDialogTeacherNew.run { - visibility = VISIBLE - text = teacher - } - } - } - teacher.isNotBlank() -> timetableDialogTeacher.text = teacher - else -> { - timetableDialogTeacherTitle.visibility = GONE - timetableDialogTeacher.visibility = GONE - } - } - } - - private fun setGroup(group: String) { - group.let { - when { - it.isNotBlank() -> timetableDialogGroup.text = it - else -> { - timetableDialogGroupTitle.visibility = GONE - timetableDialogGroup.visibility = GONE - } - } - } - } - - private fun setRoom(room: String, roomOld: String) { - when { - roomOld.isNotBlank() && roomOld != room -> { - timetableDialogRoom.run { - visibility = VISIBLE - paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG - text = roomOld - } - if (room.isNotBlank()) { - timetableDialogRoomNew.run { - visibility = VISIBLE - text = room - } - } - } - room.isNotBlank() -> timetableDialogRoom.text = room - else -> { - timetableDialogRoomTitle.visibility = GONE - timetableDialogRoom.visibility = GONE - } - } - } - - @SuppressLint("SetTextI18n") - private fun setTime(start: LocalDateTime, end: LocalDateTime) { - timetableDialogTime.text = "${start.toFormattedString("HH:mm")} - ${end.toFormattedString("HH:mm")}" - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt deleted file mode 100644 index 26eaef271..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt +++ /dev/null @@ -1,161 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.FlexibleItemDecoration -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_timetable.* -import javax.inject.Inject - -class TimetableFragment : BaseSessionFragment(), TimetableView, MainView.MainChildView, MainView.TitledView { - - @Inject - lateinit var presenter: TimetablePresenter - - @Inject - lateinit var timetableAdapter: FlexibleAdapter> - - companion object { - private const val SAVED_DATE_KEY = "CURRENT_DATE" - - fun newInstance() = TimetableFragment() - } - - override val titleStringId: Int - get() = R.string.timetable_title - - override val roomString: String - get() = getString(R.string.timetable_room) - - override val isViewEmpty: Boolean - get() = timetableAdapter.isEmpty - - override val currentStackSize: Int? - get() = (activity as? MainActivity)?.currentStackSize - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_timetable, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = timetableRecycler - presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) - } - - override fun initView() { - timetableAdapter.run { - setOnItemClickListener { presenter.onTimetableItemSelected(it) } - } - - timetableRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = timetableAdapter - addItemDecoration(FlexibleItemDecoration(context) - .withDefaultDivider() - .withDrawDividerOnLastItem(false) - ) - } - timetableSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - timetablePreviousButton.setOnClickListener { presenter.onPreviousDay() } - timetableNextButton.setOnClickListener { presenter.onNextDay() } - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.action_menu_timetable, menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return if (item.itemId == R.id.timetableMenuCompletedLessons) presenter.onCompletedLessonsSwitchSelected() - else false - } - - override fun updateData(data: List) { - timetableAdapter.updateDataSet(data, true) - } - - override fun clearData() { - timetableAdapter.clear() - } - - override fun updateNavigationDay(date: String) { - timetableNavDate.text = date - } - - override fun hideRefresh() { - timetableSwipe.isRefreshing = false - } - - override fun resetView() { - timetableRecycler.smoothScrollToPosition(0) - } - - override fun onFragmentReselected() { - 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 deleted file mode 100644 index c721401f3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt +++ /dev/null @@ -1,61 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -import android.annotation.SuppressLint -import android.graphics.Paint -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_timetable.* - -class TimetableItem(val lesson: Timetable, private val roomText: String) : - AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_timetable - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { - return ViewHolder(view, adapter) - } - - @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - holder.apply { - timetableItemNumber.text = lesson.number.toString() - timetableItemSubject.text = lesson.subject - timetableItemRoom.text = if (lesson.room.isNotBlank()) "$roomText ${lesson.room}" else "" - timetableItemTime.text = "${lesson.start.toFormattedString("HH:mm")} - ${lesson.end.toFormattedString("HH:mm")}" - timetableItemAlert.visibility = if (lesson.changes || lesson.canceled) VISIBLE else GONE - timetableItemSubject.paintFlags = - if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG - else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as TimetableItem - - if (lesson != other.lesson) return false - return true - } - - override fun hashCode(): Int { - var result = lesson.hashCode() - result = 31 * result + lesson.id.toInt() - return result - } - - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt deleted file mode 100644 index 5b837f076..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ /dev/null @@ -1,134 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.timetable.TimetableRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.isHolidays -import io.github.wulkanowy.utils.nextOrSameSchoolDay -import io.github.wulkanowy.utils.nextSchoolDay -import io.github.wulkanowy.utils.previousSchoolDay -import io.github.wulkanowy.utils.toFormattedString -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDate.now -import org.threeten.bp.LocalDate.ofEpochDay -import timber.log.Timber -import java.util.concurrent.TimeUnit.MILLISECONDS -import javax.inject.Inject - -class TimetablePresenter @Inject constructor( - private val errorHandler: SessionErrorHandler, - private val schedulers: SchedulersProvider, - private val timetableRepository: TimetableRepository, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - lateinit var currentDate: LocalDate - private set - - fun onAttachView(view: TimetableView, date: Long?) { - super.onAttachView(view) - 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, START_DATE to currentDate.toFormattedString("yyyy-MM-dd")) - }) { - Timber.i("Loading timetable result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } - } - - private fun reloadView() { - Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}") - view?.apply { - showProgress(true) - enableSwipe(false) - showContent(false) - showEmpty(false) - clearData() - showNextButton(!currentDate.plusDays(1).isHolidays) - showPreButton(!currentDate.minusDays(1).isHolidays) - updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize()) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt deleted file mode 100644 index c08961c36..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface TimetableView : BaseSessionView { - - val roomString: String - - val isViewEmpty: Boolean - - val currentStackSize: Int? - - fun initView() - - fun updateData(data: List) - - fun updateNavigationDay(date: String) - - fun clearData() - - fun hideRefresh() - - fun resetView() - - fun showEmpty(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun showPreButton(show: Boolean) - - fun showNextButton(show: Boolean) - - fun showTimetableDialog(lesson: Timetable) - - fun popView() - - fun openCompletedLessonsView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt deleted file mode 100644 index 8f7b1ec5b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt +++ /dev/null @@ -1,73 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.completed - -import android.annotation.SuppressLint -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.CompletedLesson -import kotlinx.android.synthetic.main.dialog_lesson_completed.* - -class CompletedLessonDialog : DialogFragment() { - - private lateinit var completedLesson: CompletedLesson - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(exam: CompletedLesson): CompletedLessonDialog { - return CompletedLessonDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - completedLesson = getSerializable(CompletedLessonDialog.ARGUMENT_KEY) as CompletedLesson - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_lesson_completed, container, false) - } - - @SuppressLint("SetTextI18n") - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - completedLessonDialogSubject.text = completedLesson.subject - completedLessonDialogTopic.text = completedLesson.topic - completedLessonDialogTeacher.text = completedLesson.teacher - completedLessonDialogAbsence.text = completedLesson.absence - completedLessonDialogChanges.text = completedLesson.substitution - completedLessonDialogResources.text = completedLesson.resources - - completedLesson.substitution.let { - if (it.isBlank()) { - completedLessonDialogChangesTitle.visibility = View.GONE - completedLessonDialogChanges.visibility = View.GONE - } else completedLessonDialogChanges.text = it - } - - completedLesson.absence.let { - if (it.isBlank()) { - completedLessonDialogAbsenceTitle.visibility = View.GONE - completedLessonDialogAbsence.visibility = View.GONE - } else completedLessonDialogAbsence.text = it - } - - completedLesson.resources.let { - if (it.isBlank()) { - completedLessonDialogResourcesTitle.visibility = View.GONE - completedLessonDialogResources.visibility = View.GONE - } else completedLessonDialogResources.text = it - } - - completedLessonDialogClose.setOnClickListener { dismiss() } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonItem.kt deleted file mode 100644 index 716903f55..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonItem.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.completed - -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.CompletedLesson -import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.item_completed_lesson.* - -class CompletedLessonItem(val completedLesson: CompletedLesson) : AbstractFlexibleItem() { - - override fun getLayoutRes() = R.layout.item_completed_lesson - - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): CompletedLessonItem.ViewHolder { - return CompletedLessonItem.ViewHolder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: CompletedLessonItem.ViewHolder?, position: Int, payloads: MutableList?) { - holder?.apply { - completedLessonItemNumber.text = completedLesson.number.toString() - completedLessonItemSubject.text = completedLesson.subject - completedLessonItemTopic.text = completedLesson.topic - completedLessonItemAlert.visibility = if (completedLesson.substitution.isNotEmpty()) VISIBLE else GONE - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as CompletedLessonItem - - if (completedLesson != other.completedLesson) return false - - return true - } - - override fun hashCode(): Int { - return completedLesson.hashCode() - } - - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? - get() = contentView - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt deleted file mode 100644 index 50d556859..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.completed - -import android.content.res.Resources -import com.readystatesoftware.chuck.api.ChuckCollector -import io.github.wulkanowy.api.interceptor.FeatureDisabledException -import io.github.wulkanowy.ui.base.session.SessionErrorHandler -import javax.inject.Inject - -class CompletedLessonsErrorHandler @Inject constructor( - resources: Resources, - chuckCollector: ChuckCollector -) : SessionErrorHandler(resources, chuckCollector) { - - var onFeatureDisabled: () -> Unit = {} - - override fun proceed(error: Throwable) { - when (error) { - is FeatureDisabledException -> onFeatureDisabled() - else -> super.proceed(error) - } - } - - override fun clear() { - super.clear() - onFeatureDisabled = {} - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt deleted file mode 100644 index bb328c414..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt +++ /dev/null @@ -1,124 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.completed - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.content.ContextCompat -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.ui.base.session.BaseSessionFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.setOnItemClickListener -import kotlinx.android.synthetic.main.fragment_timetable_completed.* -import javax.inject.Inject - -class CompletedLessonsFragment : BaseSessionFragment(), CompletedLessonsView, MainView.TitledView { - - @Inject - lateinit var presenter: CompletedLessonsPresenter - - @Inject - lateinit var completedLessonsAdapter: FlexibleAdapter> - - companion object { - private const val SAVED_DATE_KEY = "CURRENT_DATE" - - fun newInstance() = CompletedLessonsFragment() - } - - override val titleStringId: Int - get() = R.string.completed_lessons_title - - override val isViewEmpty - get() = completedLessonsAdapter.isEmpty - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_timetable_completed, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - messageContainer = completedLessonsRecycler - presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) - } - - override fun initView() { - completedLessonsAdapter.run { - setOnItemClickListener { presenter.onCompletedLessonsItemSelected(it) } - } - - completedLessonsRecycler.run { - layoutManager = SmoothScrollLinearLayoutManager(context) - adapter = completedLessonsAdapter - } - completedLessonsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } - completedLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() } - completedLessonsNextButton.setOnClickListener { presenter.onNextDay() } - } - - override fun updateData(data: List) { - completedLessonsAdapter.updateDataSet(data, true) - } - - override fun clearData() { - completedLessonsAdapter.clear() - } - - override fun updateNavigationDay(date: String) { - completedLessonsNavDate.text = date - } - - override fun hideRefresh() { - completedLessonsSwipe.isRefreshing = false - } - - override fun showEmpty(show: Boolean) { - completedLessonsEmpty.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showFeatureDisabled() { - context?.let { - completedLessonsInfo.text = getString(R.string.error_feature_disabled) - completedLessonsInfoImage.setImageDrawable(ContextCompat.getDrawable(it, R.drawable.ic_all_close_circle_24dp)) - } - } - - override fun showProgress(show: Boolean) { - completedLessonsProgress.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun enableSwipe(enable: Boolean) { - completedLessonsSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - completedLessonsRecycler.visibility = if (show) View.VISIBLE else View.GONE - } - - override fun showPreButton(show: Boolean) { - completedLessonsPreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showNextButton(show: Boolean) { - completedLessonsNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE - } - - override fun showCompletedLessonDialog(completedLesson: CompletedLesson) { - (activity as? MainActivity)?.showDialogFragment(CompletedLessonDialog.newInstance(completedLesson)) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putLong(CompletedLessonsFragment.SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt deleted file mode 100644 index 6a14acda5..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt +++ /dev/null @@ -1,118 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.completed - -import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.ui.base.session.BaseSessionPresenter -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper -import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.isHolidays -import io.github.wulkanowy.utils.nextOrSameSchoolDay -import io.github.wulkanowy.utils.nextSchoolDay -import io.github.wulkanowy.utils.previousSchoolDay -import io.github.wulkanowy.utils.toFormattedString -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDate.now -import org.threeten.bp.LocalDate.ofEpochDay -import timber.log.Timber -import java.util.concurrent.TimeUnit -import javax.inject.Inject - -class CompletedLessonsPresenter @Inject constructor( - private val schedulers: SchedulersProvider, - private val errorHandler: CompletedLessonsErrorHandler, - private val studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val completedLessonsRepository: CompletedLessonsRepository, - private val analytics: FirebaseAnalyticsHelper -) : BaseSessionPresenter(errorHandler) { - - lateinit var currentDate: LocalDate - private set - - fun onAttachView(view: CompletedLessonsView, date: Long?) { - super.onAttachView(view) - Timber.i("Completed lessons is attached") - view.initView() - loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay())) - reloadView() - errorHandler.onFeatureDisabled = { - this.view?.showFeatureDisabled() - Timber.i("Completed lessons feature disabled by school") - } - } - - fun onPreviousDay() { - loadData(currentDate.previousSchoolDay) - reloadView() - } - - fun onNextDay() { - loadData(currentDate.nextSchoolDay) - reloadView() - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the completed lessons") - loadData(currentDate, true) - } - - fun onCompletedLessonsItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is CompletedLessonItem) { - Timber.i("Select completed lessons item ${item.completedLesson.id}") - view?.showCompletedLessonDialog(item.completedLesson) - } - } - - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - Timber.i("Loading completed lessons data started") - currentDate = date - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .delay(200, TimeUnit.MILLISECONDS) - .flatMap { completedLessonsRepository.getCompletedLessons(it, currentDate, currentDate, forceRefresh) } - .map { items -> items.map { CompletedLessonItem(it) } } - .map { items -> items.sortedBy { it.completedLesson.number } } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - } - } - .subscribe({ - Timber.i("Loading completed lessons lessons result: Success") - view?.apply { - updateData(it) - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - } - analytics.logEvent("load_completed_lessons", "items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd")) - }) { - Timber.i("Loading completed lessons result: An exception occurred") - view?.run { showEmpty(isViewEmpty) } - errorHandler.dispatch(it) - }) - } - } - - private fun reloadView() { - Timber.i("Reload completed lessons view with the date ${currentDate.toFormattedString()}") - view?.apply { - showProgress(true) - enableSwipe(false) - showContent(false) - showEmpty(false) - clearData() - showNextButton(!currentDate.plusDays(1).isHolidays) - showPreButton(!currentDate.minusDays(1).isHolidays) - updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize()) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt deleted file mode 100644 index 9607cc476..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.completed - -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.ui.base.session.BaseSessionView - -interface CompletedLessonsView : BaseSessionView { - - val isViewEmpty: Boolean - - fun initView() - - fun updateData(data: List) - - fun clearData() - - fun updateNavigationDay(date: String) - - fun hideRefresh() - - fun showEmpty(show: Boolean) - - fun showFeatureDisabled() - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showContent(show: Boolean) - - fun showPreButton(show: Boolean) - - fun showNextButton(show: Boolean) - - fun showCompletedLessonDialog(completedLesson: CompletedLesson) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt deleted file mode 100644 index 468567f18..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt +++ /dev/null @@ -1,79 +0,0 @@ -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 - 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)) - } - - override fun onDestroy() { - super.onDestroy() - presenter.onDetachView() - } -} 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 deleted file mode 100644 index f6ec1519f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt +++ /dev/null @@ -1,53 +0,0 @@ -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 deleted file mode 100644 index 5a7bdd7c0..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt +++ /dev/null @@ -1,65 +0,0 @@ -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( - private val errorHandler: ErrorHandler, - private val schedulers: SchedulersProvider, - private val studentRepository: StudentRepository, - private val sharedPref: SharedPrefHelper -) : BasePresenter(errorHandler) { - - 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 deleted file mode 100644 index 98c800d4c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt +++ /dev/null @@ -1,18 +0,0 @@ -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 deleted file mode 100644 index b288f7b79..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt +++ /dev/null @@ -1,119 +0,0 @@ -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 deleted file mode 100644 index a462ca7b5..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt +++ /dev/null @@ -1,180 +0,0 @@ -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 new file mode 100644 index 000000000..21ce8ba5e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashActivity.java @@ -0,0 +1,50 @@ +package io.github.wulkanowy.ui.splash; + +import android.os.Bundle; +import 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 new file mode 100644 index 000000000..a1c5ad723 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashContract.java @@ -0,0 +1,20 @@ +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 new file mode 100644 index 000000000..838b411d3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashModule.java @@ -0,0 +1,13 @@ +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 new file mode 100644 index 000000000..694233481 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.java @@ -0,0 +1,30 @@ +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 new file mode 100644 index 000000000..9e6323969 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetFactory.java @@ -0,0 +1,140 @@ +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 new file mode 100644 index 000000000..aa164e29e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/TimetableWidgetProvider.java @@ -0,0 +1,112 @@ +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 deleted file mode 100644 index 93781a9fe..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/ActivityExtension.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.utils - -import android.app.Activity -import android.content.Context.INPUT_METHOD_SERVICE -import android.view.inputmethod.InputMethodManager - -fun Activity.showSoftInput() { - (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager?)?.run { - if (currentFocus != null) showSoftInput(currentFocus, 0) - } -} - -fun Activity.hideSoftInput() { - (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager?)?.run { - hideSoftInputFromWindow(window.decorView.applicationWindowToken, 0) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/AnimationUtils.java b/app/src/main/java/io/github/wulkanowy/utils/AnimationUtils.java new file mode 100644 index 000000000..041164231 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/AnimationUtils.java @@ -0,0 +1,40 @@ +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 new file mode 100644 index 000000000..2499974d5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/AppConstant.java @@ -0,0 +1,27 @@ +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 deleted file mode 100644 index c2b1efaa8..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.utils - -import io.github.wulkanowy.data.db.entities.AttendanceSummary - -/** - * [UONET+ - Zasady tworzenia podsumowań liczb uczniów obecnych i nieobecnych w tabeli frekwencji] - * (https://www.vulcan.edu.pl/vulcang_files/user/AABW/AABW-PDF/uonetplus/uonetplus_Frekwencja-liczby-obecnych-nieobecnych.pdf) - */ - -private inline val AttendanceSummary.allPresences: Double - get() = presence.toDouble() + absenceForSchoolReasons + lateness + latenessExcused - -private inline val AttendanceSummary.allAbsences: Double - get() = absence.toDouble() + absenceExcused - -fun AttendanceSummary.calculatePercentage() = calculatePercentage(allPresences, allAbsences) - -fun List.calculatePercentage(): Double { - return calculatePercentage(sumByDouble { it.allPresences }, sumByDouble { it.allAbsences }) -} - -private fun calculatePercentage(presence: Double, absence: Double): Double { - return if ((presence + absence) == 0.0) 0.0 else (presence / (presence + absence)) * 100 -} - - diff --git a/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java b/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java new file mode 100644 index 000000000..9c7b35fb4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/CommonUtils.java @@ -0,0 +1,54 @@ +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 deleted file mode 100644 index c367ecd07..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt +++ /dev/null @@ -1,28 +0,0 @@ -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 new file mode 100644 index 000000000..cd4490d60 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/DataObjectConverter.java @@ -0,0 +1,202 @@ +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 deleted file mode 100644 index caa977ec3..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/EditTextExtension.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.wulkanowy.utils - -import android.text.Editable -import android.text.TextWatcher -import android.widget.EditText - -inline fun EditText.setOnTextChangedListener(crossinline listener: () -> Unit) { - addTextChangedListener(object : TextWatcher { - override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - listener() - } - - override fun afterTextChanged(s: Editable?) {} - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} - }) -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/EntitiesCompare.java b/app/src/main/java/io/github/wulkanowy/utils/EntitiesCompare.java new file mode 100644 index 000000000..5681bad17 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/EntitiesCompare.java @@ -0,0 +1,42 @@ +package io.github.wulkanowy.utils; + +import org.apache.commons.collections4.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; + +import io.github.wulkanowy.data.db.dao.entities.Grade; + +public final class EntitiesCompare { + + private EntitiesCompare() { + throw new IllegalStateException("Utility class"); + } + + public static List compareGradeList(List newList, List oldList) { + + List addedOrUpdatedGradeList = new ArrayList<>(CollectionUtils + .removeAll(newList, oldList)); + List updatedList = new ArrayList<>(CollectionUtils + .removeAll(newList, addedOrUpdatedGradeList)); + List lastList = new ArrayList<>(); + + for (Grade grade : addedOrUpdatedGradeList) { + if (!oldList.isEmpty()) { + grade.setRead(false); + } + grade.setIsNew(true); + updatedList.add(grade); + } + + for (Grade updateGrade : updatedList) { + for (Grade oldGrade : oldList) { + if (updateGrade.equals(oldGrade)) { + updateGrade.setRead(oldGrade.getRead()); + } + } + lastList.add(updateGrade); + } + return lastList; + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/FabricUtils.java b/app/src/main/java/io/github/wulkanowy/utils/FabricUtils.java new file mode 100644 index 000000000..69897caa0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/FabricUtils.java @@ -0,0 +1,37 @@ +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/FirebaseAnalyticsHelper.kt b/app/src/main/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt deleted file mode 100644 index e7ac9c99f..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.utils - -import android.os.Bundle -import com.google.firebase.analytics.FirebaseAnalytics -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class FirebaseAnalyticsHelper @Inject constructor(private val analytics: FirebaseAnalytics) { - - fun logEvent(name: String, vararg params: Pair) { - Bundle().apply { - params.forEach { - if (it.second == null) return@forEach - when (it.second) { - is String, is String? -> putString(it.first, it.second as String) - is Int, is Int? -> putInt(it.first, it.second as Int) - is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean) - } - } - analytics.logEvent(name, this) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/FlexibleAdapterExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/FlexibleAdapterExtension.kt deleted file mode 100644 index bd6867a38..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/FlexibleAdapterExtension.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.utils - -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem - -inline fun FlexibleAdapter<*>.setOnItemClickListener(crossinline listener: (item: AbstractFlexibleItem<*>) -> Unit) { - addListener(FlexibleAdapter.OnItemClickListener { _, position -> - listener(getItem(position) as AbstractFlexibleItem<*>) - true - }) -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt deleted file mode 100644 index dcf477548..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.utils - -import androidx.fragment.app.Fragment -import com.ncapdevi.fragnav.FragNavController - -inline fun FragNavController.setOnViewChangeListener(crossinline listener: (fragment: Fragment?) -> Unit) { - transactionListener = object : FragNavController.TransactionListener { - override fun onFragmentTransaction(fragment: Fragment?, transactionType: FragNavController.TransactionType) { - listener(fragment) - } - - override fun onTabTransaction(fragment: Fragment?, index: Int) { - listener(fragment) - } - } -} - -fun FragNavController.safelyPopFragment() { - if (!isRootFragment) popFragment() -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt deleted file mode 100644 index c863b030f..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt +++ /dev/null @@ -1,74 +0,0 @@ -package io.github.wulkanowy.utils - -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.GradeSummary - -fun List.calcAverage(): Double { - var counter = 0.0 - var denominator = 0.0 - - forEach { - counter += (it.value + it.modifier) * it.weightValue - denominator += it.weightValue - } - return if (denominator != 0.0) counter / denominator else 0.0 -} - -@JvmName("calcSummaryAverage") -fun List.calcAverage(): Double { - return asSequence().mapNotNull { - if (it.finalGrade.matches("[0-6]".toRegex())) it.finalGrade.toDouble() else null - }.average().let { if (it.isNaN()) 0.0 else it } -} - -fun Grade.getBackgroundColor(theme: String): Int { - return when (theme) { - "grade_color" -> when (color) { - "000000" -> R.color.grade_black - "F04C4C" -> R.color.grade_red - "20A4F7" -> R.color.grade_blue - "6ECD07" -> R.color.grade_green - "B16CF1" -> R.color.grade_purple - else -> R.color.grade_material_default - } - "material" -> when (value) { - 6 -> R.color.grade_material_six - 5 -> R.color.grade_material_five - 4 -> R.color.grade_material_four - 3 -> R.color.grade_material_three - 2 -> R.color.grade_material_two - 1 -> R.color.grade_material_one - else -> R.color.grade_material_default - } - else -> when (value) { - 6 -> R.color.grade_vulcan_six - 5 -> R.color.grade_vulcan_five - 4 -> R.color.grade_vulcan_four - 3 -> R.color.grade_vulcan_three - 2 -> R.color.grade_vulcan_two - 1 -> R.color.grade_vulcan_one - else -> R.color.grade_vulcan_default - } - } -} - -inline val Grade.colorStringId: Int - get() { - return when (color) { - "000000" -> R.string.all_black - "F04C4C" -> R.string.all_red - "20A4F7" -> R.string.all_blue - "6ECD07" -> R.string.all_green - "B16CF1" -> R.string.all_purple - else -> R.string.all_empty_color - } - } - -fun Grade.changeModifier(plusModifier: Double, minusModifier: Double): Grade { - return when { - modifier != .0 && plusModifier != .0 && modifier > 0 -> copy(modifier = plusModifier) - modifier != .0 && minusModifier != .0 && modifier < 0 -> copy(modifier = -minusModifier) - else -> this - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/GradeUtils.java b/app/src/main/java/io/github/wulkanowy/utils/GradeUtils.java new file mode 100644 index 000000000..7b6ba193a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/GradeUtils.java @@ -0,0 +1,172 @@ +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 new file mode 100644 index 000000000..6be1b76f4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/KeyboardUtils.java @@ -0,0 +1,33 @@ +package io.github.wulkanowy.utils; + +import android.app.Activity; +import android.content.Context; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +public final class KeyboardUtils { + + private KeyboardUtils() { + throw new IllegalStateException("Utility class"); + } + + public static void hideSoftInput(Activity activity) { + InputMethodManager manager = (InputMethodManager) + activity.getSystemService(Context.INPUT_METHOD_SERVICE); + if (manager != null) { + manager.hideSoftInputFromWindow(activity.getWindow() + .getDecorView().getApplicationWindowToken(), 0); + } + } + + public static void showSoftInput(EditText editText, Context context) { + editText.setFocusable(true); + editText.setFocusableInTouchMode(true); + editText.requestFocus(); + InputMethodManager inputMethodManager = (InputMethodManager) context + .getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputMethodManager != null) { + inputMethodManager.showSoftInput(editText, 0); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/utils/LibsBuiderExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/LibsBuiderExtension.kt deleted file mode 100644 index d21b998bb..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/LibsBuiderExtension.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.wulkanowy.utils - -import android.view.View -import com.mikepenz.aboutlibraries.Libs -import com.mikepenz.aboutlibraries.LibsBuilder -import com.mikepenz.aboutlibraries.LibsConfiguration - -inline fun LibsBuilder.withOnExtraListener(crossinline listener: (Libs.SpecialButton?) -> Unit): LibsBuilder { - withListener(object : LibsConfiguration.LibsListenerImpl() { - override fun onExtraClicked(v: View?, specialButton: Libs.SpecialButton?): Boolean { - listener(specialButton) - return true - } - }) - return this -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ListExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ListExtension.kt deleted file mode 100644 index 4374aeb41..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/ListExtension.kt +++ /dev/null @@ -1,9 +0,0 @@ -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 new file mode 100644 index 000000000..6e6c701b1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.java @@ -0,0 +1,47 @@ +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 deleted file mode 100644 index 5a6cc2bfd..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt +++ /dev/null @@ -1,116 +0,0 @@ -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 com.crashlytics.android.Crashlytics -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 CrashlyticsTree : Timber.Tree() { - - override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - Crashlytics.setInt("priority", priority) - Crashlytics.setString("tag", tag) - - if (t == null) Crashlytics.log(message) - else Crashlytics.logException(t) - } -} - -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 new file mode 100644 index 000000000..a1484eb9e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/NetworkUtils.java @@ -0,0 +1,18 @@ +package io.github.wulkanowy.utils; + +import android.content.Context; +import android.net.ConnectivityManager; + +public final class NetworkUtils { + + private NetworkUtils() { + throw new IllegalStateException("Utility class"); + } + + public static boolean isOnline(Context context) { + ConnectivityManager connectivityManager = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + return connectivityManager != null && connectivityManager.getActiveNetworkInfo() != null + && connectivityManager.getActiveNetworkInfo().isConnectedOrConnecting(); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/SchedulersProvider.kt b/app/src/main/java/io/github/wulkanowy/utils/SchedulersProvider.kt deleted file mode 100644 index e426d4d5d..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/SchedulersProvider.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.wulkanowy.utils - -import io.reactivex.Scheduler -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers - -open class SchedulersProvider { - - open val mainThread: Scheduler - get() = AndroidSchedulers.mainThread() - - open val backgroundThread: Scheduler - get() = Schedulers.io() -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/SpinnerExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SpinnerExtension.kt deleted file mode 100644 index d9e34e87c..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/SpinnerExtension.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.wulkanowy.utils - -import android.view.View -import android.widget.AdapterView -import android.widget.Spinner - -/** - * @see How to keep onItemSelected from firing off on a newly instantiated Spinner? - */ -inline fun Spinner.setOnItemSelectedListener(crossinline listener: (view: View?) -> Unit) { - onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onNothingSelected(parent: AdapterView<*>?) {} - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onNothingSelected(parent: AdapterView<*>?) {} - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - listener(view) - } - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt deleted file mode 100644 index 426816f44..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt +++ /dev/null @@ -1,127 +0,0 @@ -package io.github.wulkanowy.utils - -import org.threeten.bp.DateTimeUtils -import org.threeten.bp.DayOfWeek.FRIDAY -import org.threeten.bp.DayOfWeek.MONDAY -import org.threeten.bp.DayOfWeek.SATURDAY -import org.threeten.bp.DayOfWeek.SUNDAY -import org.threeten.bp.Instant -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDateTime -import org.threeten.bp.Month -import org.threeten.bp.ZoneId -import org.threeten.bp.format.DateTimeFormatter -import org.threeten.bp.format.DateTimeFormatter.ofPattern -import org.threeten.bp.format.TextStyle.FULL_STANDALONE -import org.threeten.bp.temporal.TemporalAdjusters.firstInMonth -import org.threeten.bp.temporal.TemporalAdjusters.next -import org.threeten.bp.temporal.TemporalAdjusters.previous -import java.util.Date -import java.util.Locale - -private const val DATE_PATTERN = "dd.MM.yyyy" - -fun Date.toLocalDate(): LocalDate { - return Instant.ofEpochMilli(this.time).atZone(ZoneId.systemDefault()).toLocalDate() -} - -fun Date.toLocalDateTime(): LocalDateTime { - return Instant.ofEpochMilli(this.time).atZone(ZoneId.systemDefault()).toLocalDateTime() -} - -fun String.toLocalDate(format: String = DATE_PATTERN): LocalDate { - return LocalDate.parse(this, DateTimeFormatter.ofPattern(format)) -} - -fun LocalDate.toFormattedString(format: String = DATE_PATTERN): String = this.format(ofPattern(format)) - -fun LocalDateTime.toFormattedString(format: String = DATE_PATTERN): String = this.format(ofPattern(format)) - -fun LocalDateTime.toDate(): Date = DateTimeUtils.toDate(atZone(ZoneId.systemDefault()).toInstant()) - -/** - * https://github.com/ThreeTen/threetenbp/issues/55 - */ - -fun Month.getFormattedName(): String { - return getDisplayName(FULL_STANDALONE, Locale.getDefault()) - .let { - when (it) { - "stycznia" -> "Styczeń" - "lutego" -> "Luty" - "marca" -> "Marzec" - "kwietnia" -> "Kwiecień" - "maja" -> "Maj" - "czerwca" -> "Czerwiec" - "lipca" -> "Lipiec" - "sierpnia" -> "Sierpień" - "września" -> "Wrzesień" - "października" -> "Październik" - "listopada" -> "Listopad" - "grudnia" -> "Grudzień" - else -> it - } - } -} - -inline val LocalDate.nextSchoolDay: LocalDate - get() { - return when (this.dayOfWeek) { - FRIDAY, SATURDAY, SUNDAY -> this.with(next(MONDAY)) - else -> this.plusDays(1) - } - } - -inline val LocalDate.previousSchoolDay: LocalDate - get() { - return when (this.dayOfWeek) { - SATURDAY, SUNDAY, MONDAY -> this.with(previous(FRIDAY)) - else -> this.minusDays(1) - } - } - -inline val LocalDate.nextOrSameSchoolDay: LocalDate - get() { - return when (this.dayOfWeek) { - SATURDAY, SUNDAY -> this.with(next(MONDAY)) - else -> this - } - } - -inline val LocalDate.previousOrSameSchoolDay: LocalDate - get() { - return when (this.dayOfWeek) { - SATURDAY, SUNDAY -> this.with(previous(FRIDAY)) - else -> this - } - } - -inline val LocalDate.weekDayName: String - get() = this.format(ofPattern("EEEE", Locale.getDefault())) - -inline val LocalDate.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 new file mode 100644 index 000000000..c61c7e0dc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt @@ -0,0 +1,97 @@ +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 deleted file mode 100644 index 6a5ad880a..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.utils - -import androidx.viewpager.widget.ViewPager - -inline fun ViewPager.setOnSelectPageListener(crossinline selectListener: (position: Int) -> Unit) { - addOnPageChangeListener(object : ViewPager.OnPageChangeListener { - override fun onPageSelected(position: Int) { - selectListener(position) - } - override fun onPageScrollStateChanged(state: Int) {} - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} - }) -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java b/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java new file mode 100644 index 000000000..447214164 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/async/AbstractTask.java @@ -0,0 +1,63 @@ +package io.github.wulkanowy.utils.async; + +import android.os.AsyncTask; + +import 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 new file mode 100644 index 000000000..0d33c57cf --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/async/AsyncListeners.java @@ -0,0 +1,23 @@ +package io.github.wulkanowy.utils.async; + +public interface AsyncListeners { + + interface OnRefreshListener { + + void onDoInBackgroundRefresh() throws Exception; + + void onCanceledRefreshAsync(); + + void onEndRefreshAsync(boolean result, Exception exception); + + } + + interface OnFirstLoadingListener { + + void onDoInBackgroundLoading() throws Exception; + + void onCanceledLoadingAsync(); + + void onEndLoadingAsync(boolean result, Exception exception); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/CryptoException.java b/app/src/main/java/io/github/wulkanowy/utils/security/CryptoException.java new file mode 100644 index 000000000..1ee4a9fa8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/security/CryptoException.java @@ -0,0 +1,9 @@ +package io.github.wulkanowy.utils.security; + + +public class CryptoException extends Exception { + + public CryptoException(String message) { + super(message); + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java new file mode 100644 index 000000000..8e425a894 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.java @@ -0,0 +1,176 @@ +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 deleted file mode 100644 index 24e6d3ffa..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt +++ /dev/null @@ -1,149 +0,0 @@ -@file:Suppress("DEPRECATION") - -package io.github.wulkanowy.utils.security - -import android.annotation.TargetApi -import android.content.Context -import android.os.Build.VERSION.SDK_INT -import android.os.Build.VERSION_CODES.JELLY_BEAN_MR2 -import android.os.Build.VERSION_CODES.M -import android.security.KeyPairGeneratorSpec -import android.security.keystore.KeyGenParameterSpec -import android.security.keystore.KeyProperties.DIGEST_SHA256 -import android.security.keystore.KeyProperties.DIGEST_SHA512 -import android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_OAEP -import android.security.keystore.KeyProperties.PURPOSE_DECRYPT -import android.security.keystore.KeyProperties.PURPOSE_ENCRYPT -import android.util.Base64 -import android.util.Base64.DEFAULT -import android.util.Base64.decode -import android.util.Base64.encode -import android.util.Base64.encodeToString -import timber.log.Timber -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.math.BigInteger -import java.nio.charset.Charset -import java.security.KeyPairGenerator -import java.security.KeyStore -import java.security.spec.MGF1ParameterSpec.SHA1 -import java.util.Calendar -import java.util.Calendar.YEAR -import javax.crypto.Cipher -import javax.crypto.Cipher.DECRYPT_MODE -import javax.crypto.Cipher.ENCRYPT_MODE -import javax.crypto.CipherInputStream -import javax.crypto.CipherOutputStream -import javax.crypto.spec.OAEPParameterSpec -import javax.crypto.spec.PSource.PSpecified -import javax.security.auth.x500.X500Principal - -private const val KEYSTORE_NAME = "AndroidKeyStore" - -private const val KEY_ALIAS = "wulkanowy_password" - -private val KEY_CHARSET = Charset.forName("UTF-8") - -private val isKeyPairExists: Boolean - get() = keyStore.getKey(KEY_ALIAS, null) != null - -private val keyStore: KeyStore - get() = KeyStore.getInstance(KEYSTORE_NAME).apply { load(null) } - -private val cipher: Cipher - get() { - return if (SDK_INT >= M) Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "AndroidKeyStoreBCWorkaround") - else Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL") - } - -fun encrypt(plainText: String, context: Context): String { - if (plainText.isEmpty()) throw ScramblerException("Text to be encrypted is empty") - - if (SDK_INT < JELLY_BEAN_MR2) { - return String(Base64.encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET) - } - - return try { - if (!isKeyPairExists) generateKeyPair(context) - - cipher.let { - if (SDK_INT >= M) { - OAEPParameterSpec("SHA-256", "MGF1", SHA1, PSpecified.DEFAULT).let { spec -> - it.init(ENCRYPT_MODE, keyStore.getCertificate(KEY_ALIAS).publicKey, spec) - } - } else it.init(ENCRYPT_MODE, keyStore.getCertificate(KEY_ALIAS).publicKey) - - ByteArrayOutputStream().let { output -> - CipherOutputStream(output, it).apply { - write(plainText.toByteArray(KEY_CHARSET)) - close() - } - encodeToString(output.toByteArray(), DEFAULT) - } - } - } catch (exception: Exception) { - Timber.e(exception, "An error occurred while encrypting text") - String(encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET) - } -} - -fun decrypt(cipherText: String): String { - if (cipherText.isEmpty()) throw ScramblerException("Text to be encrypted is empty") - - return try { - if (SDK_INT < JELLY_BEAN_MR2 || cipherText.length < 250) { - return String(decode(cipherText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET) - } - - if (!isKeyPairExists) throw ScramblerException("KeyPair doesn't exist") - - cipher.let { - if (SDK_INT >= M) { - OAEPParameterSpec("SHA-256", "MGF1", SHA1, PSpecified.DEFAULT).let { spec -> - it.init(DECRYPT_MODE, keyStore.getKey(KEY_ALIAS, null), spec) - } - } else it.init(DECRYPT_MODE, keyStore.getKey(KEY_ALIAS, null)) - - CipherInputStream(ByteArrayInputStream(decode(cipherText, DEFAULT)), it).let { input -> - val values = ArrayList() - var nextByte = 0 - while ({ nextByte = input.read(); nextByte }() != -1) { - values.add(nextByte.toByte()) - } - val bytes = ByteArray(values.size) - for (i in bytes.indices) { - bytes[i] = values[i] - } - String(bytes, 0, bytes.size, KEY_CHARSET) - } - } - } catch (e: Exception) { - throw ScramblerException("An error occurred while decrypting text", e) - } -} - -@TargetApi(JELLY_BEAN_MR2) -private fun generateKeyPair(context: Context) { - (if (SDK_INT >= M) { - KeyGenParameterSpec.Builder(KEY_ALIAS, PURPOSE_DECRYPT or PURPOSE_ENCRYPT) - .setDigests(DIGEST_SHA256, DIGEST_SHA512) - .setEncryptionPaddings(ENCRYPTION_PADDING_RSA_OAEP) - .setCertificateSerialNumber(BigInteger.TEN) - .setCertificateSubject(X500Principal("CN=Wulkanowy")) - .build() - } else { - KeyPairGeneratorSpec.Builder(context) - .setAlias(KEY_ALIAS) - .setSubject(X500Principal("CN=Wulkanowy")) - .setSerialNumber(BigInteger.TEN) - .setStartDate(Calendar.getInstance().time) - .setEndDate(Calendar.getInstance().apply { add(YEAR, 99) }.time) - .build() - }).let { - KeyPairGenerator.getInstance("RSA", KEYSTORE_NAME).apply { - initialize(it) - genKeyPair() - } - } - Timber.i("A new KeyPair has been generated") -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/ScramblerException.kt b/app/src/main/java/io/github/wulkanowy/utils/security/ScramblerException.kt deleted file mode 100644 index 59f830fad..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/security/ScramblerException.kt +++ /dev/null @@ -1,6 +0,0 @@ -package io.github.wulkanowy.utils.security - -class ScramblerException : Exception { - constructor(message: String, cause: Throwable) : super(message, cause) - constructor(message: String) : super(message) -} diff --git a/app/src/main/play/contact-email.txt b/app/src/main/play/contactEmail similarity index 100% rename from app/src/main/play/contact-email.txt rename to app/src/main/play/contactEmail diff --git a/app/src/main/play/contact-phone.txt b/app/src/main/play/contactPhone similarity index 100% rename from app/src/main/play/contact-phone.txt rename to app/src/main/play/contactPhone diff --git a/app/src/main/play/contact-website.txt b/app/src/main/play/contactWebsite similarity index 100% rename from app/src/main/play/contact-website.txt rename to app/src/main/play/contactWebsite diff --git a/app/src/main/play/default-language.txt b/app/src/main/play/defaultLanguage similarity index 100% rename from app/src/main/play/default-language.txt rename to app/src/main/play/defaultLanguage diff --git a/app/src/main/play/listings/pl-PL/graphics/feature-graphic/feature.png b/app/src/main/play/listings/pl-PL/graphics/feature-graphic/feature.png deleted file mode 100644 index 094b25b6c..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/feature-graphic/feature.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/icon/icon.png b/app/src/main/play/listings/pl-PL/graphics/icon/icon.png deleted file mode 100644 index 8c33c2f92..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/icon/icon.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/account-switcher.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/account-switcher.png deleted file mode 100644 index 8a52d8de8..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/account-switcher.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-dialog.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-dialog.png deleted file mode 100644 index c65085747..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-dialog.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-statistics.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-statistics.png deleted file mode 100644 index 376fc3c3f..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/attendance-statistics.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/grades.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/grades.png deleted file mode 100644 index 6b4089b02..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/grades.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/more.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/more.png deleted file mode 100644 index c3dc523ff..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/more.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-dialog.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-dialog.png deleted file mode 100644 index 511433d45..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-dialog.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-widget.png b/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-widget.png deleted file mode 100644 index 1d5024707..000000000 Binary files a/app/src/main/play/listings/pl-PL/graphics/phone-screenshots/timetable-widget.png and /dev/null differ diff --git a/app/src/main/play/listings/pl-PL/short-description.txt b/app/src/main/play/listings/pl-PL/short-description.txt deleted file mode 100644 index c850c159a..000000000 --- a/app/src/main/play/listings/pl-PL/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -Nieoficjalna aplikacja ucznia i rodzica dla dziennika VULCAN UONET+ diff --git a/app/src/main/play/listings/pl-PL/title.txt b/app/src/main/play/listings/pl-PL/title.txt deleted file mode 100644 index 2d53a62a8..000000000 --- a/app/src/main/play/listings/pl-PL/title.txt +++ /dev/null @@ -1 +0,0 @@ -Wulkanowy Dzienniczek diff --git a/app/src/main/play/pl-PL/listing/featureGraphic/feature.png b/app/src/main/play/pl-PL/listing/featureGraphic/feature.png new file mode 100644 index 000000000..15e6ebbfc Binary files /dev/null and b/app/src/main/play/pl-PL/listing/featureGraphic/feature.png differ diff --git a/app/src/main/play/listings/pl-PL/full-description.txt b/app/src/main/play/pl-PL/listing/fulldescription similarity index 51% rename from app/src/main/play/listings/pl-PL/full-description.txt rename to app/src/main/play/pl-PL/listing/fulldescription index d33cb56f2..4af6238a1 100644 --- a/app/src/main/play/listings/pl-PL/full-description.txt +++ b/app/src/main/play/pl-PL/listing/fulldescription @@ -1,14 +1,10 @@ Aplikacja jest we wczesnej fazie rozwoju, ciągle pracujemy nad kolejnymi funkcjami. Wyróżnione cechy i funkcje: -- obliczanie średniej ważonej, -- procentowy podgląd frekwencji, -- szczęśliwy numerek, -- podgląd lekcji zrealizowanych, -- ciemny motyw. -- brak reklam, -- tryb offline, -- powiadomienia. +- Całkowicie darmowa i otwarta (brak jakichkolwiek reklam i mikropłatności) +- Powiadomienia +- Tryb offline +- Aktywne wsparcie i rozwój GitHub: https://github.com/wulkanowy/wulkanowy Discord: https://discord.gg/vccAQBr diff --git a/app/src/main/play/pl-PL/listing/icon/icon.png b/app/src/main/play/pl-PL/listing/icon/icon.png new file mode 100644 index 000000000..7cad5c2f4 Binary files /dev/null and b/app/src/main/play/pl-PL/listing/icon/icon.png 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 new file mode 100644 index 000000000..3f4ceb7e1 Binary files /dev/null and b/app/src/main/play/pl-PL/listing/phoneScreenshots/attendance-dialog.png 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 new file mode 100644 index 000000000..5a055ff42 Binary files /dev/null and b/app/src/main/play/pl-PL/listing/phoneScreenshots/exams.png 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 new file mode 100644 index 000000000..58c22c519 Binary files /dev/null and b/app/src/main/play/pl-PL/listing/phoneScreenshots/grades.png 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 new file mode 100644 index 000000000..832db0b68 Binary files /dev/null and b/app/src/main/play/pl-PL/listing/phoneScreenshots/timetable-widget.png 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 new file mode 100644 index 000000000..e9e66bbb9 Binary files /dev/null and b/app/src/main/play/pl-PL/listing/phoneScreenshots/timetable.png differ diff --git a/app/src/main/play/pl-PL/listing/shortdescription b/app/src/main/play/pl-PL/listing/shortdescription new file mode 100644 index 000000000..b5fc7491b --- /dev/null +++ b/app/src/main/play/pl-PL/listing/shortdescription @@ -0,0 +1 @@ +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 new file mode 100644 index 000000000..e491efb90 --- /dev/null +++ b/app/src/main/play/pl-PL/listing/title @@ -0,0 +1 @@ +Wulkanowy Dzienniczek UONET+ diff --git a/app/src/main/play/pl-PL/listing/video b/app/src/main/play/pl-PL/listing/video new file mode 100644 index 000000000..e69de29bb diff --git a/app/src/main/play/pl-PL/whatsnew b/app/src/main/play/pl-PL/whatsnew new file mode 100644 index 000000000..ff412b228 --- /dev/null +++ b/app/src/main/play/pl-PL/whatsnew @@ -0,0 +1,5 @@ +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 deleted file mode 100644 index d1f8f1e7f..000000000 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ /dev/null @@ -1,10 +0,0 @@ -Wersja 0.8.3 - -Naprawiliśmy: -- rzadkie problemy z wczytywaniem treści wiadomości -- rzadkie problemy z wyświetlaniem poprawnego nauczyciela przy zastępstwach - -Zmieniliśmy: -- tekst wskazówki przy wprowadzaniu symbolu w formularzu logowania - -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 deleted file mode 100644 index 00538168f..000000000 --- a/app/src/main/res/drawable-anydpi-v24/ic_stat_notify_lucky_number.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable-hdpi/ic_stat_notify_grade.png b/app/src/main/res/drawable-hdpi/ic_notify_grade.png similarity index 100% rename from app/src/main/res/drawable-hdpi/ic_stat_notify_grade.png rename to app/src/main/res/drawable-hdpi/ic_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 deleted file mode 100644 index b91c4ae6c..000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_stat_notify_lucky_number.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_stat_notify_message.png b/app/src/main/res/drawable-hdpi/ic_stat_notify_message.png deleted file mode 100644 index 86d63c587..000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_stat_notify_message.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_stat_notify_note.png b/app/src/main/res/drawable-hdpi/ic_stat_notify_note.png deleted file mode 100644 index b49e4ad2c..000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_stat_notify_note.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_widget_account.png b/app/src/main/res/drawable-hdpi/ic_widget_account.png deleted file mode 100644 index 4cb5ac89e..000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_widget_account.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_stat_notify_grade.png b/app/src/main/res/drawable-mdpi/ic_notify_grade.png similarity index 100% rename from app/src/main/res/drawable-mdpi/ic_stat_notify_grade.png rename to app/src/main/res/drawable-mdpi/ic_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 deleted file mode 100644 index bfced4eb0..000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_stat_notify_lucky_number.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_stat_notify_message.png b/app/src/main/res/drawable-mdpi/ic_stat_notify_message.png deleted file mode 100644 index 73a1653a0..000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_stat_notify_message.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_stat_notify_note.png b/app/src/main/res/drawable-mdpi/ic_stat_notify_note.png deleted file mode 100644 index 3498f71c3..000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_stat_notify_note.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_widget_account.png b/app/src/main/res/drawable-mdpi/ic_widget_account.png deleted file mode 100644 index 237a6b6c2..000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_widget_account.png and /dev/null differ diff --git a/app/src/main/res/drawable-night/ic_all_divider.xml b/app/src/main/res/drawable-night/ic_border.xml similarity index 100% rename from app/src/main/res/drawable-night/ic_all_divider.xml rename to app/src/main/res/drawable-night/ic_border.xml diff --git a/app/src/main/res/drawable/layer_splash_background.xml b/app/src/main/res/drawable-v15/splash_background.xml similarity index 51% rename from app/src/main/res/drawable/layer_splash_background.xml rename to app/src/main/res/drawable-v15/splash_background.xml index 64812f151..c58c374a1 100644 --- a/app/src/main/res/drawable/layer_splash_background.xml +++ b/app/src/main/res/drawable-v15/splash_background.xml @@ -1,12 +1,14 @@ + - + + + android:gravity="center" + android:src="@mipmap/ic_launcher"/> diff --git a/app/src/main/res/drawable-v23/img_splash_logo.png b/app/src/main/res/drawable-v23/img_splash_logo.png deleted file mode 100644 index 61489d81b..000000000 Binary files a/app/src/main/res/drawable-v23/img_splash_logo.png and /dev/null differ diff --git a/app/src/main/res/drawable-v23/layer_splash_background.xml b/app/src/main/res/drawable-v23/layer_splash_background.xml deleted file mode 100644 index 5fa58e900..000000000 --- a/app/src/main/res/drawable-v23/layer_splash_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable-v23/splash_background.xml b/app/src/main/res/drawable-v23/splash_background.xml new file mode 100644 index 000000000..611bb2a7a --- /dev/null +++ b/app/src/main/res/drawable-v23/splash_background.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_notify_grade.png b/app/src/main/res/drawable-xhdpi/ic_notify_grade.png similarity index 100% rename from app/src/main/res/drawable-xhdpi/ic_stat_notify_grade.png rename to app/src/main/res/drawable-xhdpi/ic_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 deleted file mode 100644 index 49e502ac0..000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_stat_notify_lucky_number.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_notify_message.png b/app/src/main/res/drawable-xhdpi/ic_stat_notify_message.png deleted file mode 100644 index b373ffd4a..000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_stat_notify_message.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_notify_note.png b/app/src/main/res/drawable-xhdpi/ic_stat_notify_note.png deleted file mode 100644 index 5aa30e5f7..000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_stat_notify_note.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_widget_account.png b/app/src/main/res/drawable-xhdpi/ic_widget_account.png deleted file mode 100644 index 0ea798a8a..000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_widget_account.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_grade.png b/app/src/main/res/drawable-xxhdpi/ic_notify_grade.png similarity index 100% rename from app/src/main/res/drawable-xxhdpi/ic_stat_notify_grade.png rename to app/src/main/res/drawable-xxhdpi/ic_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 deleted file mode 100644 index 9bab13731..000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_lucky_number.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_message.png b/app/src/main/res/drawable-xxhdpi/ic_stat_notify_message.png deleted file mode 100644 index dc15bbd96..000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_message.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_note.png b/app/src/main/res/drawable-xxhdpi/ic_stat_notify_note.png deleted file mode 100644 index db3ec517b..000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_stat_notify_note.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_widget_account.png b/app/src/main/res/drawable-xxhdpi/ic_widget_account.png deleted file mode 100644 index 0f8933b17..000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_widget_account.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_message.png b/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_message.png deleted file mode 100644 index 3b40ad3f1..000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_message.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_note.png b/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_note.png deleted file mode 100644 index 06a9299a9..000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_note.png and /dev/null differ diff --git a/app/src/main/res/drawable/background_rounded_corner.xml b/app/src/main/res/drawable/background_rounded_corner.xml deleted file mode 100644 index 116973b54..000000000 --- a/app/src/main/res/drawable/background_rounded_corner.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_account_circular_border.xml b/app/src/main/res/drawable/ic_account_circular_border.xml deleted file mode 100644 index 8ba66975a..000000000 --- a/app/src/main/res/drawable/ic_account_circular_border.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_message_forward_24dp.xml b/app/src/main/res/drawable/ic_action_menu_semester.xml similarity index 56% rename from app/src/main/res/drawable/ic_message_forward_24dp.xml rename to app/src/main/res/drawable/ic_action_menu_semester.xml index 2b7c32c75..b7acb9df9 100644 --- a/app/src/main/res/drawable/ic_message_forward_24dp.xml +++ b/app/src/main/res/drawable/ic_action_menu_semester.xml @@ -2,9 +2,9 @@ android:width="24dp" android:height="24dp" android:tint="#FFFFFF" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:viewportHeight="24.0" + android:viewportWidth="24.0"> + android:pathData="M10,18h4v-2h-4v2zM3,6v2h18L21,6L3,6zM6,13h12v-2L6,11v2z" /> diff --git a/app/src/main/res/drawable/ic_all_about_24dp.xml b/app/src/main/res/drawable/ic_action_menu_summary.xml similarity index 72% rename from app/src/main/res/drawable/ic_all_about_24dp.xml rename to app/src/main/res/drawable/ic_action_menu_summary.xml index 550f312e8..1b2a93b8c 100644 --- a/app/src/main/res/drawable/ic_all_about_24dp.xml +++ b/app/src/main/res/drawable/ic_action_menu_summary.xml @@ -4,9 +4,10 @@ android:viewportHeight="24" android:viewportWidth="24"> + android:fillColor="#FFF" + 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_round_note_24dp.xml b/app/src/main/res/drawable/ic_alert_24dp.xml similarity index 97% rename from app/src/main/res/drawable/ic_all_round_note_24dp.xml rename to app/src/main/res/drawable/ic_alert_24dp.xml index f4f1f2c52..1c41b556d 100644 --- a/app/src/main/res/drawable/ic_all_round_note_24dp.xml +++ b/app/src/main/res/drawable/ic_alert_24dp.xml @@ -7,4 +7,4 @@ android:fillColor="@color/colorPrimary" android:pathData="M13 13h-2V7h2m0 10h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z" /> - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_all_account_24dp.xml b/app/src/main/res/drawable/ic_all_account_24dp.xml deleted file mode 100644 index dea10cb34..000000000 --- a/app/src/main/res/drawable/ic_all_account_24dp.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_all_close_circle_24dp.xml b/app/src/main/res/drawable/ic_all_close_circle_24dp.xml deleted file mode 100644 index 3809c7979..000000000 --- a/app/src/main/res/drawable/ic_all_close_circle_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_all_divider.xml b/app/src/main/res/drawable/ic_border.xml similarity index 100% rename from app/src/main/res/drawable/ic_all_divider.xml rename to app/src/main/res/drawable/ic_border.xml diff --git a/app/src/main/res/drawable/ic_all_note_24dp.xml b/app/src/main/res/drawable/ic_exclamation_24dp.xml similarity index 96% rename from app/src/main/res/drawable/ic_all_note_24dp.xml rename to app/src/main/res/drawable/ic_exclamation_24dp.xml index 988df22c0..149504585 100644 --- a/app/src/main/res/drawable/ic_all_note_24dp.xml +++ b/app/src/main/res/drawable/ic_exclamation_24dp.xml @@ -7,4 +7,4 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index cfec6390b..c5ecc7474 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,27 +1,17 @@ - - - - - - - + android:width="108dp" + android:height="108dp" + android:viewportHeight="51.67464" + android:viewportWidth="51.67464"> + + diff --git a/app/src/main/res/drawable/ic_login_outlined_border.xml b/app/src/main/res/drawable/ic_login_outlined_border.xml deleted file mode 100644 index c5d3c6867..000000000 --- a/app/src/main/res/drawable/ic_login_outlined_border.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_menu_main_attendance_24dp.xml b/app/src/main/res/drawable/ic_menu_attendance_24dp.xml similarity index 69% rename from app/src/main/res/drawable/ic_menu_main_attendance_24dp.xml rename to app/src/main/res/drawable/ic_menu_attendance_24dp.xml index eccc338fc..c0df16d60 100644 --- a/app/src/main/res/drawable/ic_menu_main_attendance_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_attendance_24dp.xml @@ -6,6 +6,7 @@ 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_exam_24dp.xml b/app/src/main/res/drawable/ic_menu_exams_24dp.xml similarity index 100% rename from app/src/main/res/drawable/ic_menu_main_exam_24dp.xml rename to app/src/main/res/drawable/ic_menu_exams_24dp.xml diff --git a/app/src/main/res/drawable/ic_menu_main_grade_26dp.xml b/app/src/main/res/drawable/ic_menu_grade_26dp.xml similarity index 100% rename from app/src/main/res/drawable/ic_menu_main_grade_26dp.xml rename to app/src/main/res/drawable/ic_menu_grade_26dp.xml diff --git a/app/src/main/res/drawable/ic_menu_grade_semester_24dp.xml b/app/src/main/res/drawable/ic_menu_grade_semester_24dp.xml deleted file mode 100644 index 2a03f6300..000000000 --- a/app/src/main/res/drawable/ic_menu_grade_semester_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_main_attendance_summary_24dp.xml b/app/src/main/res/drawable/ic_menu_main_attendance_summary_24dp.xml deleted file mode 100644 index 13cfc8fad..000000000 --- a/app/src/main/res/drawable/ic_menu_main_attendance_summary_24dp.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_main_homework_24dp.xml b/app/src/main/res/drawable/ic_menu_main_homework_24dp.xml deleted file mode 100644 index 449f0e28a..000000000 --- a/app/src/main/res/drawable/ic_menu_main_homework_24dp.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_menu_main_lessons_completed_24dp.xml b/app/src/main/res/drawable/ic_menu_main_lessons_completed_24dp.xml deleted file mode 100644 index c0664c370..000000000 --- a/app/src/main/res/drawable/ic_menu_main_lessons_completed_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_main_more_24dp.xml b/app/src/main/res/drawable/ic_menu_main_more_24dp.xml deleted file mode 100644 index 26932b2b2..000000000 --- a/app/src/main/res/drawable/ic_menu_main_more_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_main_note_24dp.xml b/app/src/main/res/drawable/ic_menu_main_note_24dp.xml deleted file mode 100644 index 6b3172baf..000000000 --- a/app/src/main/res/drawable/ic_menu_main_note_24dp.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_menu_other_24dp.xml b/app/src/main/res/drawable/ic_menu_other_24dp.xml new file mode 100644 index 000000000..a07747245 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_other_24dp.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/main/res/drawable/ic_menu_send_message_24dp.xml b/app/src/main/res/drawable/ic_menu_send_message_24dp.xml deleted file mode 100644 index fcc5acfb0..000000000 --- a/app/src/main/res/drawable/ic_menu_send_message_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_main_timetable_24dp.xml b/app/src/main/res/drawable/ic_menu_timetable_24dp.xml similarity index 53% rename from app/src/main/res/drawable/ic_menu_main_timetable_24dp.xml rename to app/src/main/res/drawable/ic_menu_timetable_24dp.xml index 1594285f9..63880977b 100644 --- a/app/src/main/res/drawable/ic_menu_main_timetable_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_timetable_24dp.xml @@ -1,10 +1,11 @@ + + android:viewportHeight="24" + android:viewportWidth="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_message_delete_24dp.xml b/app/src/main/res/drawable/ic_message_delete_24dp.xml deleted file mode 100644 index 3760de238..000000000 --- a/app/src/main/res/drawable/ic_message_delete_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_message_reply_24dp.xml b/app/src/main/res/drawable/ic_message_reply_24dp.xml deleted file mode 100644 index 010cb250b..000000000 --- a/app/src/main/res/drawable/ic_message_reply_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_more_lucky_number_24dp.xml b/app/src/main/res/drawable/ic_more_lucky_number_24dp.xml deleted file mode 100644 index f2e6152d8..000000000 --- a/app/src/main/res/drawable/ic_more_lucky_number_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_more_messages_24dp.xml b/app/src/main/res/drawable/ic_more_messages_24dp.xml deleted file mode 100644 index 65f32169d..000000000 --- a/app/src/main/res/drawable/ic_more_messages_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_more_settings_24dp.xml b/app/src/main/res/drawable/ic_more_settings_24dp.xml deleted file mode 100644 index fc12cdcfe..000000000 --- a/app/src/main/res/drawable/ic_more_settings_24dp.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_send_message_24dp.xml b/app/src/main/res/drawable/ic_send_message_24dp.xml deleted file mode 100644 index 0d82394be..000000000 --- a/app/src/main/res/drawable/ic_send_message_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_timetable_swap_30dp.xml b/app/src/main/res/drawable/ic_swap_30dp.xml similarity index 100% rename from app/src/main/res/drawable/ic_timetable_swap_30dp.xml rename to app/src/main/res/drawable/ic_swap_30dp.xml diff --git a/app/src/main/res/drawable/ic_widget_chevron_24dp.png b/app/src/main/res/drawable/ic_widget_chevron_24dp.png deleted file mode 100644 index 34345521a..000000000 Binary files a/app/src/main/res/drawable/ic_widget_chevron_24dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_widget_clover_24dp.png b/app/src/main/res/drawable/ic_widget_clover_24dp.png deleted file mode 100644 index b1a7832a2..000000000 Binary files a/app/src/main/res/drawable/ic_widget_clover_24dp.png and /dev/null 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 deleted file mode 100644 index 539b0a598..000000000 Binary files a/app/src/main/res/drawable/img_luckynumber_widget_preview.png and /dev/null differ diff --git a/app/src/main/res/drawable/img_splash_512px.png b/app/src/main/res/drawable/img_splash_512px.png new file mode 100644 index 000000000..9d8f403c2 Binary files /dev/null and b/app/src/main/res/drawable/img_splash_512px.png differ diff --git a/app/src/main/res/drawable/img_splash_logo.png b/app/src/main/res/drawable/img_splash_logo.png deleted file mode 100644 index fb521bf65..000000000 Binary files a/app/src/main/res/drawable/img_splash_logo.png and /dev/null differ diff --git a/app/src/main/res/drawable/img_timetable_widget_preview.png b/app/src/main/res/drawable/img_timetable_widget_preview.png deleted file mode 100644 index 550260258..000000000 Binary files a/app/src/main/res/drawable/img_timetable_widget_preview.png and /dev/null differ diff --git a/app/src/main/res/drawable/splash_background.xml b/app/src/main/res/drawable/splash_background.xml new file mode 100644 index 000000000..0b79b6729 --- /dev/null +++ b/app/src/main/res/drawable/splash_background.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/widget_timetable_preview.png b/app/src/main/res/drawable/widget_timetable_preview.png new file mode 100644 index 000000000..d9f3538c4 Binary files /dev/null and b/app/src/main/res/drawable/widget_timetable_preview.png differ diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 1a86fa925..4a6f08b22 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -1,10 +1,137 @@ - + android:layout_height="match_parent" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + tools:context="io.github.wulkanowy.ui.login.LoginActivity"> - + - + android:layout_height="match_parent" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:layout_alignParentTop="true" + android:gravity="center" + android:visibility="gone"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +