diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..57bbb4d48 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +[*.{kt,kts}] +ktlint_code_style = android_studio +max_line_length = off +ktlint_standard_property-naming = disabled +ij_kotlin_allow_trailing_comma_on_call_site = false +ij_kotlin_allow_trailing_comma = false +indent_size = 4 + diff --git a/.gitignore b/.gitignore index 1aea15ff0..e84411d0c 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,9 @@ captures/ *.iml .idea +# Kotlin +.kotlin + # Keystore files *.jks diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 90e466fee..000000000 --- a/app/build.gradle +++ /dev/null @@ -1,330 +0,0 @@ -/* - * This file is part of the UNES Open Source Project. - * UNES is licensed under the GNU GPLv3. - * - * Copyright (c) 2020. João Paulo Sena - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -import java.nio.file.Files - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-parcelize' -apply plugin: 'androidx.navigation.safeargs' -apply plugin: 'dagger.hilt.android.plugin' -apply plugin: 'org.jmailen.kotlinter' -apply plugin: 'com.github.triplet.play' -apply plugin: 'com.google.firebase.crashlytics' -apply plugin: 'com.mikepenz.aboutlibraries.plugin' - -android { - compileSdk 34 - - defaultConfig { - applicationId 'com.forcetower.uefs' - minSdkVersion 21 - targetSdkVersion 34 - def (code, name) = buildVersion() - versionCode code - versionName name - multiDexEnabled true - - buildConfigField "String", "SIECOMP_TIMEZONE", "\"America/Bahia\"" - buildConfigField "String", "SIECOMP_DAY1_START", "\"2019-10-16T08:00:00-03:00\"" - buildConfigField "String", "SIECOMP_DAY1_END", "\"2019-10-16T17:30:00-03:00\"" - buildConfigField "String", "SIECOMP_DAY2_START", "\"2019-10-17T08:00:00-03:00\"" - buildConfigField "String", "SIECOMP_DAY2_END", "\"2019-10-17T17:30:00-03:00\"" - buildConfigField "String", "SIECOMP_DAY3_START", "\"2019-10-18T08:00:00-03:00\"" - buildConfigField "String", "SIECOMP_DAY3_END", "\"2019-10-18T17:30:00-03:00\"" - buildConfigField "String", "SIECOMP_DAY4_START", "\"2019-10-21T08:00:00-03:00\"" - buildConfigField "String", "SIECOMP_DAY4_END", "\"2019-10-21T17:30:00-03:00\"" - buildConfigField "String", "SIECOMP_DAY5_START", "\"2019-10-22T08:00:00-03:00\"" - buildConfigField "String", "SIECOMP_DAY5_END", "\"2019-10-22T17:30:00-03:00\"" - - buildConfigField "String", "UEFS_DEFAULT_PROXY", "\"10.65.16.2:3128\"" - - javaCompileOptions { - annotationProcessorOptions { - arguments += [ - "room.schemaLocation": "$projectDir/schemas".toString(), - "room.incremental" : "true" - ] - } - } - - ndk { - abiFilters "arm64-v8a", "armeabi", "armeabi-v7a", "mips", "mips64", "x86", "x86_64" - } - - ndkVersion "21.3.6528147" - - compileOptions { - sourceCompatibility = 17 - targetCompatibility = 17 - } - } - - signingConfigs { - release { - def password = System.getenv("UNES_KEYSTORE_PASSWORD") - if (password == null) - password = "android" - - def alias = System.getenv("UNES_KEYSTORE_ALIAS") - if (alias == null) - alias = "androiddebugkey" - - def keyPass = System.getenv("UNES_KEYSTORE_PRIVATE_KEY_PASSWORD") - if (keyPass == null) - keyPass = "android" - - def signFile = rootProject.file("sign.jks") - if (!signFile.exists()) - signFile = rootProject.file("debug.keystore") - - storeFile signFile - storePassword password - keyAlias alias - keyPassword keyPass - } - debug { - storeFile rootProject.file("debug.keystore") - storePassword "android" - keyAlias "androiddebugkey" - keyPassword "android" - } - } - - buildTypes { - def mapsKey = System.getenv("UNES_MAPS_KEY") - if (mapsKey == null) { - mapsKey = "AIzaSyAIb0g7GrjLgOwRqmKHhBxbxWKjct8IF8Y" - } - release { - signingConfig signingConfigs.release - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - minifyEnabled true - manifestPlaceholders = [crashlyticsEnabled: true] - resValue("string", "google_maps_key", mapsKey) - } - debug { - applicationIdSuffix '.debug' - manifestPlaceholders = [crashlyticsEnabled: false] - resValue("string", "google_maps_key", "AIzaSyAIb0g7GrjLgOwRqmKHhBxbxWKjct8IF8Y") - } - } - - buildFeatures { - dataBinding true - buildConfig true - } - - kapt { - correctErrorTypes true - javacOptions { - option("-Xmaxerrs", 1000) - } - } - - compileOptions { - coreLibraryDesugaringEnabled true - } - - dynamicFeatures = [ - ":dynamic-features:aeri", - ":dynamic-features:dashboard", - ":dynamic-features:conference", - ":dynamic-features:event", - ":dynamic-features:map", - ":dynamic-features:disciplines" - ] - - testOptions { - unitTests.returnDefaultValues = true - } - lint { - abortOnError true - checkDependencies true - disable 'MissingTranslation', 'InvalidPackage', 'NullSafeMutableLiveData' - ignoreTestSources true - } - namespace 'com.forcetower.uefs' -} - -play { - def branch = 'git rev-parse --abbrev-ref HEAD'.execute([], project.rootDir).text.trim() - def publishTrack = "internal" - if (branch == "main") publishTrack = "production" - - serviceAccountCredentials.set(rootProject.file("unes_uefs_publisher.json")) - track.set(publishTrack) - defaultToAppBundles.set(true) -} - -kotlinter { - -} - -dependencies { - implementation 'androidx.navigation:navigation-fragment-ktx:2.7.7' - implementation 'androidx.navigation:navigation-ui-ktx:2.7.7' - coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:$coreDesugar" - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation project(path: ':bypass') - implementation project(path: ':core') - - // Auto start - implementation 'com.github.judemanutd:autostarter:1.1.0' - - // Portal scrapper - implementation "dev.forcetower.unes:juice:$juice" - implementation "dev.forcetower.unes:snowpiercer:$snowpiercer" - - // Kotlin - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.8.0' - - // Android X - implementation "androidx.core:core-ktx:$ktx" - implementation "androidx.annotation:annotation:$annotation" - implementation "androidx.appcompat:appcompat:$app_compat" - implementation "androidx.swiperefreshlayout:swiperefreshlayout:$swipe_refresh" - implementation "androidx.constraintlayout:constraintlayout:$constraint_layout" - implementation "com.google.android.material:material:$google_material" - implementation "androidx.palette:palette-ktx:$pallete" - implementation "androidx.browser:browser:$browser" - implementation "androidx.preference:preference-ktx:$preference" - implementation "com.google.android.flexbox:flexbox:$flexbox" - implementation "androidx.fragment:fragment-ktx:$fragment" - implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' - - // Architecture - api "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle" - api "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle" - api "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle" - api "androidx.lifecycle:lifecycle-common-java8:$lifecycle" - api "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle" - api "androidx.lifecycle:lifecycle-service:$lifecycle" - - // Paging - implementation "androidx.paging:paging-runtime-ktx:$paging" - - // Room - kapt "androidx.room:room-compiler:$room" - - // Dependency injection - implementation "com.google.dagger:hilt-android:$hilt_dagger" - implementation "androidx.hilt:hilt-work:$hilt_androidx" - kapt "com.google.dagger:hilt-compiler:$hilt_dagger" - kapt "androidx.hilt:hilt-compiler:$hilt_androidx" - - // Firebase - implementation platform('com.google.firebase:firebase-bom:32.8.1') - implementation "com.google.firebase:firebase-auth-ktx" - implementation "com.google.firebase:firebase-analytics-ktx" - implementation "com.google.firebase:firebase-messaging-ktx" - implementation "com.google.firebase:firebase-firestore-ktx" - implementation "com.google.firebase:firebase-storage-ktx" - implementation "com.google.firebase:firebase-config-ktx" - implementation "com.google.firebase:firebase-functions-ktx" - implementation "com.google.firebase:firebase-crashlytics-ktx" - implementation "com.firebaseui:firebase-ui-storage:$firebase_ui_storage" - - // passkeys - implementation("androidx.credentials:credentials:1.3.0-alpha02") - implementation("androidx.credentials:credentials-play-services-auth:1.3.0-alpha02") - - debugImplementation "com.github.chuckerteam.chucker:library:4.0.0" - releaseImplementation "com.github.chuckerteam.chucker:library-no-op:4.0.0" - - // Logs - implementation "com.jakewharton.timber:timber:$timber" - - // Google Play - implementation "com.google.android.gms:play-services-games-v2:20.0.0" - implementation "com.google.android.gms:play-services-auth:$gp_auth" - implementation "com.google.android.gms:play-services-location:$gp_location" - implementation "com.android.billingclient:billing:$gp_billing_client" - implementation 'com.google.android.play:review-ktx:2.0.1' - implementation 'com.google.android.play:app-update-ktx:2.1.0' - implementation 'com.google.android.play:feature-delivery-ktx:2.1.0' - - // Image - implementation "com.github.bumptech.glide:glide:$glide" - kapt "com.github.bumptech.glide:compiler:$glide" - implementation "com.airbnb.android:lottie:$lottie" - - // About Libraries - implementation "com.mikepenz:aboutlibraries-core:$about_libraries" - implementation "com.mikepenz:aboutlibraries:$about_libraries" - - // Others - implementation "com.google.code.gson:gson:$gson" - implementation "org.jsoup:jsoup:$jsoup" - implementation "com.github.PhilJay:MPAndroidChart:$chart_view" -// implementation "pub.devrel:easypermissions:$easy_permissions" - implementation "com.ramotion.cardslider:card-slider:$card_slider" - implementation "com.github.arimorty:floatingsearchview:$floating_search" - implementation "io.reactivex.rxjava2:rxkotlin:$rx_kotlin" - implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.3' - - implementation 'com.google.android.gms:play-services-maps:18.2.0' - - testImplementation 'junit:junit:4.13.2' - testImplementation 'io.mockk:mockk:1.13.10' - testImplementation 'androidx.arch.core:core-testing:2.2.0' - testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0' - - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' -} - -task buildNotes { - doFirst { - try { - def branch = 'git rev-parse --abbrev-ref HEAD'.execute([], project.rootDir).text.trim() - if (branch == "main") { - def lastTag = "git describe --abbrev=0".execute([], project.rootDir).text.trim() - def tagMessage = "git tag -l --format=%(contents) ${lastTag}".execute([], project.rootDir).text.trim() - def production = new File(project.rootDir, "app/src/main/play/release-notes/pt-BR/production.txt") - production.text = tagMessage - } else if (branch == "development") { - def message = "git log -1 --pretty=%B".execute([], project.rootDir).text.trim().split(":").reverse()[0] - def internal = new File(project.rootDir, "app/src/main/play/release-notes/pt-BR/internal.txt") - internal.text = message - } - } catch (Exception ignored) { - System.err.println("No git installed on the machine or not on a git repo. UNES will not generate release notes") - } - } -} - -afterEvaluate { - def publish = tasks.findByName("publishReleaseBundle") - if (publish != null) { - publish.configure { - dependsOn("buildNotes") - } - } -} - -def googleServices = file("google-services.json") -if (!googleServices.exists()) { - Files.copy(file("google-services-mock.json").toPath(), googleServices.toPath()) -} - -apply plugin: 'com.google.gms.google-services' diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 000000000..b0926a9f8 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,290 @@ +/* + * This file is part of the UNES Open Source Project. + * UNES is licensed under the GNU GPLv3. + * + * Copyright (c) 2020. João Paulo Sena + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import dev.forcetower.gradle.utils.buildVersion +import dev.forcetower.gradle.utils.runCommand +import java.nio.file.Files + +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.kapt) + alias(libs.plugins.kotlin.parcelize) + alias(libs.plugins.androidx.navigation.safe.args) + alias(libs.plugins.hilt.android.gradle) + alias(libs.plugins.kotlinter.gradle) + alias(libs.plugins.play.publisher) + alias(libs.plugins.firebase.crashlytics.gradle) + alias(libs.plugins.google.ksp) + alias(libs.plugins.androidx.room) + alias(libs.plugins.aboutlibraries) + alias(libs.plugins.google.services) +} + +android { + compileSdk = 34 + + defaultConfig { + applicationId = "com.forcetower.uefs" + minSdk = 21 + targetSdk = 34 + val (code, name) = buildVersion() + versionCode = code + versionName = name + multiDexEnabled = true + + buildConfigField("String", "SIECOMP_TIMEZONE", "\"America/Bahia\"") + buildConfigField("String", "SIECOMP_DAY1_START", "\"2019-10-16T08:00:00-03:00\"") + buildConfigField("String", "SIECOMP_DAY1_END", "\"2019-10-16T17:30:00-03:00\"") + buildConfigField("String", "SIECOMP_DAY2_START", "\"2019-10-17T08:00:00-03:00\"") + buildConfigField("String", "SIECOMP_DAY2_END", "\"2019-10-17T17:30:00-03:00\"") + buildConfigField("String", "SIECOMP_DAY3_START", "\"2019-10-18T08:00:00-03:00\"") + buildConfigField("String", "SIECOMP_DAY3_END", "\"2019-10-18T17:30:00-03:00\"") + buildConfigField("String", "SIECOMP_DAY4_START", "\"2019-10-21T08:00:00-03:00\"") + buildConfigField("String", "SIECOMP_DAY4_END", "\"2019-10-21T17:30:00-03:00\"") + buildConfigField("String", "SIECOMP_DAY5_START", "\"2019-10-22T08:00:00-03:00\"") + buildConfigField("String", "SIECOMP_DAY5_END", "\"2019-10-22T17:30:00-03:00\"") + buildConfigField("String", "UEFS_DEFAULT_PROXY", "\"10.65.16.2:3128\"") + + ndk { + abiFilters += listOf("arm64-v8a", "armeabi", "armeabi-v7a", "mips", "mips64", "x86", "x86_64") + } + + ndkVersion = "21.3.6528147" + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + } + + signingConfigs { + create("release") { + var password = System.getenv("UNES_KEYSTORE_PASSWORD") + if (password == null) + password = "android" + + var alias = System.getenv("UNES_KEYSTORE_ALIAS") + if (alias == null) + alias = "androiddebugkey" + + var keyPass = System.getenv("UNES_KEYSTORE_PRIVATE_KEY_PASSWORD") + if (keyPass == null) + keyPass = "android" + + var signFile = rootProject.file("sign.jks") + if (!signFile.exists()) + signFile = rootProject.file("debug.keystore") + + storeFile = signFile + storePassword = password + keyAlias = alias + keyPassword = keyPass + } + getByName("debug") { + storeFile = rootProject.file("debug.keystore") + storePassword = "android" + keyAlias = "androiddebugkey" + keyPassword = "android" + } + } + + buildTypes { + var mapsKey = System.getenv("UNES_MAPS_KEY") + if (mapsKey == null) { + mapsKey = "AIzaSyAIb0g7GrjLgOwRqmKHhBxbxWKjct8IF8Y" + } + getByName("release") { + signingConfig = signingConfigs.getByName("release") + proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") + isMinifyEnabled = true + manifestPlaceholders += mapOf("crashlyticsEnabled" to true) + resValue("string", "google_maps_key", mapsKey) + } + getByName("debug") { + applicationIdSuffix = ".debug" + manifestPlaceholders += mapOf("crashlyticsEnabled" to false) + resValue("string", "google_maps_key", "AIzaSyAIb0g7GrjLgOwRqmKHhBxbxWKjct8IF8Y") + } + } + + buildFeatures { + dataBinding = true + buildConfig = true + } + + kapt { + correctErrorTypes = true + javacOptions { + option("-Xmaxerrs", 1000) + } + } + + compileOptions { + isCoreLibraryDesugaringEnabled = true + } + + dynamicFeatures += listOf( + ":dynamic-features:aeri", + ":dynamic-features:dashboard", + ":dynamic-features:conference", + ":dynamic-features:event", + ":dynamic-features:map", + ":dynamic-features:disciplines" + ) + + testOptions.unitTests.isReturnDefaultValues = true + + lint { + abortOnError = true + checkDependencies = true + disable += setOf("MissingTranslation", "InvalidPackage", "NullSafeMutableLiveData") + ignoreTestSources = true + } + namespace = "com.forcetower.uefs" +} + +play { + val branch = "git rev-parse --abbrev-ref HEAD".runCommand(project.rootDir).trim() + var publishTrack = "internal" + if (branch == "main") publishTrack = "production" + + serviceAccountCredentials.set(rootProject.file("unes_uefs_publisher.json")) + track.set(publishTrack) + defaultToAppBundles.set(true) +} + +room { + schemaDirectory("$projectDir/schemas") +} + +kotlinter { + +} + +dependencies { + coreLibraryDesugaring(libs.android.tools.desugar) + implementation(project(":bypass")) + implementation(project(":core")) + implementation(libs.androidx.navigation.fragment.ktx) + implementation(libs.androidx.navigation.ui.ktx) + implementation(libs.autostarter) + implementation(libs.juice) + implementation(libs.snowpiercer) + implementation(libs.kotlin.stdlib) + implementation(libs.kotlinx.coroutines.play.services) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.annotation) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.swiperefreshlayout) + implementation(libs.androidx.constraintlayout) + implementation(libs.material) + implementation(libs.androidx.palette.ktx) + implementation(libs.androidx.browser) + implementation(libs.androidx.preference.ktx) + implementation(libs.flexbox) + implementation(libs.androidx.fragment.ktx) + implementation(libs.listenablefuture) + api(libs.androidx.viewmodel) + api(libs.androidx.lifecycle.livedata.ktx) + api(libs.androidx.lifecycle.runtime.ktx) + api(libs.androidx.lifecycle.common.java8) + api(libs.androidx.lifecycle.reactivestreams.ktx) + api(libs.androidx.lifecycle.service) + api(libs.androidx.datastore.preferences) + implementation(libs.androidx.paging.runtime.ktx) + ksp(libs.androidx.room.compiler) + implementation(libs.hilt.android) + implementation(libs.androidx.hilt.work) + ksp(libs.androidx.hilt.compiler) + ksp(libs.hilt.compiler) + implementation(platform(libs.firebase.bom)) + implementation(libs.firebase.analytics.ktx) + implementation(libs.firebase.messaging.ktx) + implementation(libs.firebase.firestore.ktx) + implementation(libs.firebase.storage.ktx) + implementation(libs.firebase.config.ktx) + implementation(libs.firebase.functions.ktx) + implementation(libs.firebase.crashlytics.ktx) + implementation(libs.firebase.ui.storage) + implementation(libs.androidx.credentials) + implementation(libs.androidx.credentials.play.services.auth) + debugImplementation(libs.chucker) + releaseImplementation(libs.chucker.no.op) + implementation(libs.timber) + implementation(libs.play.services.games.v2) + implementation(libs.play.services.auth) + implementation(libs.play.services.location) + implementation(libs.billing) + implementation(libs.review.ktx) + implementation(libs.app.update.ktx) + implementation(libs.feature.delivery.ktx) + implementation(libs.glide) + ksp(libs.glide.compiler) + implementation(libs.lottie) + implementation(libs.aboutlibraries) + implementation(libs.aboutlibraries.core) + implementation(libs.gson) + implementation(libs.jsoup) + implementation(libs.mpandroidchart) + implementation(libs.card.slider) + implementation(libs.floatingsearchview) + implementation(libs.rxkotlin) + implementation(libs.taptargetview) + implementation(libs.play.services.maps) + implementation(libs.materialdatetimepicker) + testImplementation(libs.junit) + testImplementation(libs.mockk) + testImplementation(libs.androidx.core.testing) + testImplementation(libs.kotlinx.coroutines.test) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} + +val buildNotesTask = tasks.create("buildNotes") { + doFirst { + try { + val branch = "git rev-parse --abbrev-ref HEAD".runCommand(project.rootDir).trim() + if (branch == "main") { + val lastTag = "git describe --abbrev=0".runCommand(project.rootDir).trim() + val tagMessage = "git tag -l --format=%(contents) $lastTag".runCommand(project.rootDir).trim() + val production = File(project.rootDir, "app/src/main/play/release-notes/pt-BR/production.txt") + production.writeText(tagMessage) + } else if (branch == "development") { + val message = "git log -1 --pretty=%B".runCommand(project.rootDir).trim().split(":").reversed()[0] + val internal = File(project.rootDir, "app/src/main/play/release-notes/pt-BR/internal.txt") + internal.writeText(message) + } + } catch (ignored: Exception) { + System.err.println("No git installed on the machine or not on a git repo. UNES will not generate release notes") + } + } +} + +afterEvaluate { + val publish = tasks.findByName("publishReleaseBundle") + publish?.dependsOn(buildNotesTask) +} + +val googleServices = file("google-services.json") +if (!googleServices.exists()) { + Files.copy(file("google-services-mock.json").toPath(), googleServices.toPath()) +} + diff --git a/app/schemas/com.forcetower.uefs.core.storage.database.UDatabase/53.json b/app/schemas/com.forcetower.uefs.core.storage.database.UDatabase/53.json new file mode 100644 index 000000000..822ab5a8e --- /dev/null +++ b/app/schemas/com.forcetower.uefs.core.storage.database.UDatabase/53.json @@ -0,0 +1,3108 @@ +{ + "formatVersion": 1, + "database": { + "version": 53, + "identityHash": "78e31a488be749b13e1092f8ed1f1338", + "entities": [ + { + "tableName": "AccessToken", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` TEXT NOT NULL, `token` TEXT NOT NULL, `refreshToken` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "refreshToken", + "columnName": "refreshToken", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Access", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `valid` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "valid", + "columnName": "valid", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Profile", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `email` TEXT, `score` REAL NOT NULL, `calc_score` REAL NOT NULL, `course` INTEGER, `imageUrl` TEXT, `sagres_id` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `me` INTEGER NOT NULL, `mocked` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "score", + "columnName": "score", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "calcScore", + "columnName": "calc_score", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "course", + "columnName": "course", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sagresId", + "columnName": "sagres_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "me", + "columnName": "me", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mocked", + "columnName": "mocked", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Profile_sagres_id", + "unique": true, + "columnNames": [ + "sagres_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Profile_sagres_id` ON `${TABLE_NAME}` (`sagres_id`)" + }, + { + "name": "index_Profile_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Profile_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semester", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `sagres_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `codename` TEXT NOT NULL, `start` INTEGER, `end` INTEGER, `start_class` INTEGER, `end_class` INTEGER)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sagresId", + "columnName": "sagres_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "codename", + "columnName": "codename", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "startClass", + "columnName": "start_class", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endClass", + "columnName": "end_class", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Semester_sagres_id", + "unique": true, + "columnNames": [ + "sagres_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semester_sagres_id` ON `${TABLE_NAME}` (`sagres_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Message", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `content` TEXT NOT NULL, `sagres_id` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `sender_profile` INTEGER NOT NULL, `sender_name` TEXT, `notified` INTEGER NOT NULL, `discipline` TEXT, `uuid` TEXT NOT NULL, `code_discipline` TEXT, `html` INTEGER NOT NULL, `date_string` TEXT, `processing_time` INTEGER, `hash_message` INTEGER, `attachmentName` TEXT, `attachmentLink` TEXT)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sagresId", + "columnName": "sagres_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderProfile", + "columnName": "sender_profile", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notified", + "columnName": "notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "discipline", + "columnName": "discipline", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "codeDiscipline", + "columnName": "code_discipline", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "html", + "columnName": "html", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateString", + "columnName": "date_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "processingTime", + "columnName": "processing_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hashMessage", + "columnName": "hash_message", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attachmentName", + "columnName": "attachmentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attachmentLink", + "columnName": "attachmentLink", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Message_hash_message", + "unique": true, + "columnNames": [ + "hash_message" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Message_hash_message` ON `${TABLE_NAME}` (`hash_message`)" + }, + { + "name": "index_Message_sagres_id", + "unique": true, + "columnNames": [ + "sagres_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Message_sagres_id` ON `${TABLE_NAME}` (`sagres_id`)" + }, + { + "name": "index_Message_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Message_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "CalendarItem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `message` TEXT NOT NULL, `date` TEXT NOT NULL, `uuid` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_CalendarItem_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_CalendarItem_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Discipline", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `code` TEXT NOT NULL, `credits` INTEGER NOT NULL, `department` TEXT, `resume` TEXT, `short_text` TEXT, `uuid` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "credits", + "columnName": "credits", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "department", + "columnName": "department", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "resume", + "columnName": "resume", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shortText", + "columnName": "short_text", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Discipline_code", + "unique": true, + "columnNames": [ + "code" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Discipline_code` ON `${TABLE_NAME}` (`code`)" + }, + { + "name": "index_Discipline_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Discipline_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Class", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `discipline_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `status` TEXT, `final_score` REAL, `partial_score` REAL, `uuid` TEXT NOT NULL, `missedClasses` INTEGER NOT NULL, `lastClass` TEXT NOT NULL, `nextClass` TEXT NOT NULL, `schedule_only` INTEGER NOT NULL, FOREIGN KEY(`discipline_id`) REFERENCES `Discipline`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`semester_id`) REFERENCES `Semester`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "disciplineId", + "columnName": "discipline_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "finalScore", + "columnName": "final_score", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "partialScore", + "columnName": "partial_score", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "missedClasses", + "columnName": "missedClasses", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastClass", + "columnName": "lastClass", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "nextClass", + "columnName": "nextClass", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scheduleOnly", + "columnName": "schedule_only", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Class_semester_id", + "unique": false, + "columnNames": [ + "semester_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_Class_semester_id` ON `${TABLE_NAME}` (`semester_id`)" + }, + { + "name": "index_Class_discipline_id_semester_id", + "unique": true, + "columnNames": [ + "discipline_id", + "semester_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Class_discipline_id_semester_id` ON `${TABLE_NAME}` (`discipline_id`, `semester_id`)" + }, + { + "name": "index_Class_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Class_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [ + { + "table": "Discipline", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "discipline_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Semester", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "semester_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassGroup", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `class_id` INTEGER NOT NULL, `group` TEXT NOT NULL, `teacher` TEXT, `credits` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `draft` INTEGER NOT NULL, `ignored` INTEGER NOT NULL, `teacher_id` INTEGER, `sagresId` INTEGER, `teacherEmail` TEXT, FOREIGN KEY(`class_id`) REFERENCES `Class`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`teacher_id`) REFERENCES `Teacher`(`uid`) ON UPDATE CASCADE ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "credits", + "columnName": "credits", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "draft", + "columnName": "draft", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ignored", + "columnName": "ignored", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacherId", + "columnName": "teacher_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sagresId", + "columnName": "sagresId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "teacherEmail", + "columnName": "teacherEmail", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassGroup_class_id_group", + "unique": true, + "columnNames": [ + "class_id", + "group" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassGroup_class_id_group` ON `${TABLE_NAME}` (`class_id`, `group`)" + }, + { + "name": "index_ClassGroup_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassGroup_uuid` ON `${TABLE_NAME}` (`uuid`)" + }, + { + "name": "index_ClassGroup_teacher_id", + "unique": false, + "columnNames": [ + "teacher_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassGroup_teacher_id` ON `${TABLE_NAME}` (`teacher_id`)" + } + ], + "foreignKeys": [ + { + "table": "Class", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "class_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Teacher", + "onDelete": "SET NULL", + "onUpdate": "CASCADE", + "columns": [ + "teacher_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassAbsence", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `class_id` INTEGER NOT NULL, `profile_id` INTEGER NOT NULL, `sequence` INTEGER NOT NULL, `description` TEXT NOT NULL, `date` TEXT NOT NULL, `grouping` TEXT NOT NULL, `uuid` TEXT NOT NULL, `notified` INTEGER NOT NULL, FOREIGN KEY(`class_id`) REFERENCES `Class`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`profile_id`) REFERENCES `Profile`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "profileId", + "columnName": "profile_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sequence", + "columnName": "sequence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grouping", + "columnName": "grouping", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notified", + "columnName": "notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassAbsence_profile_id", + "unique": false, + "columnNames": [ + "profile_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassAbsence_profile_id` ON `${TABLE_NAME}` (`profile_id`)" + }, + { + "name": "index_ClassAbsence_class_id", + "unique": false, + "columnNames": [ + "class_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassAbsence_class_id` ON `${TABLE_NAME}` (`class_id`)" + }, + { + "name": "index_ClassAbsence_class_id_profile_id_sequence_grouping", + "unique": true, + "columnNames": [ + "class_id", + "profile_id", + "sequence", + "grouping" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassAbsence_class_id_profile_id_sequence_grouping` ON `${TABLE_NAME}` (`class_id`, `profile_id`, `sequence`, `grouping`)" + }, + { + "name": "index_ClassAbsence_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassAbsence_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [ + { + "table": "Class", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "class_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Profile", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "profile_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassLocation", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `group_id` INTEGER NOT NULL, `profile_id` INTEGER NOT NULL, `starts_at` TEXT NOT NULL, `ends_at` TEXT NOT NULL, `day` TEXT NOT NULL, `room` TEXT, `modulo` TEXT, `campus` TEXT, `uuid` TEXT NOT NULL, `hidden_on_schedule` INTEGER NOT NULL, `startsAtInt` INTEGER NOT NULL, `endsAtInt` INTEGER NOT NULL, `dayInt` INTEGER NOT NULL, FOREIGN KEY(`group_id`) REFERENCES `ClassGroup`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`profile_id`) REFERENCES `Profile`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupId", + "columnName": "group_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "profileId", + "columnName": "profile_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "startsAt", + "columnName": "starts_at", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "endsAt", + "columnName": "ends_at", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "day", + "columnName": "day", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "modulo", + "columnName": "modulo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "campus", + "columnName": "campus", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hiddenOnSchedule", + "columnName": "hidden_on_schedule", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "startsAtInt", + "columnName": "startsAtInt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endsAtInt", + "columnName": "endsAtInt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dayInt", + "columnName": "dayInt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassLocation_group_id_day_starts_at_ends_at_profile_id", + "unique": true, + "columnNames": [ + "group_id", + "day", + "starts_at", + "ends_at", + "profile_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassLocation_group_id_day_starts_at_ends_at_profile_id` ON `${TABLE_NAME}` (`group_id`, `day`, `starts_at`, `ends_at`, `profile_id`)" + }, + { + "name": "index_ClassLocation_profile_id", + "unique": false, + "columnNames": [ + "profile_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassLocation_profile_id` ON `${TABLE_NAME}` (`profile_id`)" + }, + { + "name": "index_ClassLocation_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassLocation_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [ + { + "table": "ClassGroup", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "group_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Profile", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "profile_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassItem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `group_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `situation` TEXT, `subject` TEXT, `date` TEXT, `number_of_materials` INTEGER NOT NULL, `material_links` TEXT NOT NULL, `is_new` INTEGER NOT NULL, `uuid` TEXT NOT NULL, FOREIGN KEY(`group_id`) REFERENCES `ClassGroup`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupId", + "columnName": "group_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "situation", + "columnName": "situation", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "numberOfMaterials", + "columnName": "number_of_materials", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "materialLinks", + "columnName": "material_links", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isNew", + "columnName": "is_new", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassItem_group_id_number", + "unique": true, + "columnNames": [ + "group_id", + "number" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassItem_group_id_number` ON `${TABLE_NAME}` (`group_id`, `number`)" + }, + { + "name": "index_ClassItem_number_of_materials", + "unique": false, + "columnNames": [ + "number_of_materials" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassItem_number_of_materials` ON `${TABLE_NAME}` (`number_of_materials`)" + }, + { + "name": "index_ClassItem_situation", + "unique": false, + "columnNames": [ + "situation" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassItem_situation` ON `${TABLE_NAME}` (`situation`)" + }, + { + "name": "index_ClassItem_date", + "unique": false, + "columnNames": [ + "date" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassItem_date` ON `${TABLE_NAME}` (`date`)" + }, + { + "name": "index_ClassItem_is_new", + "unique": false, + "columnNames": [ + "is_new" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassItem_is_new` ON `${TABLE_NAME}` (`is_new`)" + }, + { + "name": "index_ClassItem_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassItem_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [ + { + "table": "ClassGroup", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "group_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassMaterial", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `group_id` INTEGER NOT NULL, `class_item_id` INTEGER, `name` TEXT NOT NULL, `link` TEXT NOT NULL, `is_new` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `notified` INTEGER NOT NULL, FOREIGN KEY(`group_id`) REFERENCES `ClassGroup`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`class_item_id`) REFERENCES `ClassItem`(`uid`) ON UPDATE CASCADE ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupId", + "columnName": "group_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classItemId", + "columnName": "class_item_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isNew", + "columnName": "is_new", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notified", + "columnName": "notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassMaterial_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_ClassMaterial_link", + "unique": false, + "columnNames": [ + "link" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_link` ON `${TABLE_NAME}` (`link`)" + }, + { + "name": "index_ClassMaterial_group_id", + "unique": false, + "columnNames": [ + "group_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_group_id` ON `${TABLE_NAME}` (`group_id`)" + }, + { + "name": "index_ClassMaterial_class_item_id", + "unique": false, + "columnNames": [ + "class_item_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_class_item_id` ON `${TABLE_NAME}` (`class_item_id`)" + }, + { + "name": "index_ClassMaterial_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassMaterial_uuid` ON `${TABLE_NAME}` (`uuid`)" + }, + { + "name": "index_ClassMaterial_is_new", + "unique": false, + "columnNames": [ + "is_new" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_is_new` ON `${TABLE_NAME}` (`is_new`)" + }, + { + "name": "index_ClassMaterial_name_link_group_id", + "unique": true, + "columnNames": [ + "name", + "link", + "group_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassMaterial_name_link_group_id` ON `${TABLE_NAME}` (`name`, `link`, `group_id`)" + } + ], + "foreignKeys": [ + { + "table": "ClassGroup", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "group_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "ClassItem", + "onDelete": "SET NULL", + "onUpdate": "CASCADE", + "columns": [ + "class_item_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "Grade", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` TEXT, `grade` TEXT, `grouping` INTEGER NOT NULL, `groupingName` TEXT NOT NULL, `notified` INTEGER NOT NULL, FOREIGN KEY(`class_id`) REFERENCES `Class`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "grouping", + "columnName": "grouping", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupingName", + "columnName": "groupingName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notified", + "columnName": "notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Grade_class_id", + "unique": false, + "columnNames": [ + "class_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_Grade_class_id` ON `${TABLE_NAME}` (`class_id`)" + }, + { + "name": "index_Grade_name_class_id_grouping", + "unique": true, + "columnNames": [ + "name", + "class_id", + "grouping" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Grade_name_class_id_grouping` ON `${TABLE_NAME}` (`name`, `class_id`, `grouping`)" + } + ], + "foreignKeys": [ + { + "table": "Class", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "class_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "Course", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `image` TEXT, `since` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "since", + "columnName": "since", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SagresDocument", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` TEXT NOT NULL, `name` TEXT NOT NULL, `downloaded` INTEGER NOT NULL, `downloading` INTEGER NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "downloaded", + "columnName": "downloaded", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "downloading", + "columnName": "downloading", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_SagresDocument_type", + "unique": true, + "columnNames": [ + "type" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SagresDocument_type` ON `${TABLE_NAME}` (`type`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "SyncRegistry", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uuid` TEXT NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER, `completed` INTEGER NOT NULL, `success` INTEGER NOT NULL, `error` INTEGER NOT NULL, `executor` TEXT NOT NULL, `message` TEXT NOT NULL, `networkType` INTEGER NOT NULL, `network` TEXT NOT NULL, `skipped` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "completed", + "columnName": "completed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "success", + "columnName": "success", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "executor", + "columnName": "executor", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "networkType", + "columnName": "networkType", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "network", + "columnName": "network", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "skipped", + "columnName": "skipped", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_SyncRegistry_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SyncRegistry_uuid` ON `${TABLE_NAME}` (`uuid`)" + }, + { + "name": "index_SyncRegistry_start", + "unique": true, + "columnNames": [ + "start" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SyncRegistry_start` ON `${TABLE_NAME}` (`start`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Teacher", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `email` TEXT, `sagresId` INTEGER, `department` TEXT, `uuid` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sagresId", + "columnName": "sagresId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "department", + "columnName": "department", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Teacher_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Teacher_uuid` ON `${TABLE_NAME}` (`uuid`)" + }, + { + "name": "index_Teacher_sagresId", + "unique": true, + "columnNames": [ + "sagresId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Teacher_sagresId` ON `${TABLE_NAME}` (`sagresId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "SDemandOffer", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `code` TEXT NOT NULL, `name` TEXT NOT NULL, `selected` INTEGER NOT NULL, `category` TEXT NOT NULL, `hours` INTEGER NOT NULL, `completed` INTEGER NOT NULL, `available` INTEGER NOT NULL, `current` INTEGER NOT NULL, `selectable` INTEGER NOT NULL, `unavailable` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "selected", + "columnName": "selected", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hours", + "columnName": "hours", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "completed", + "columnName": "completed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "available", + "columnName": "available", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "selectable", + "columnName": "selectable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unavailable", + "columnName": "unavailable", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SagresFlags", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `demand_open` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "demandOpen", + "columnName": "demand_open", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Contributor", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `login` TEXT NOT NULL, `total` INTEGER NOT NULL, `name` TEXT NOT NULL, `image` TEXT, `link` TEXT, `url` TEXT, `bio` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "login", + "columnName": "login", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "total", + "columnName": "total", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "bio", + "columnName": "bio", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_Contributor_login", + "unique": true, + "columnNames": [ + "login" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Contributor_login` ON `${TABLE_NAME}` (`login`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "ServiceRequest", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service` TEXT NOT NULL, `date` TEXT NOT NULL, `amount` INTEGER NOT NULL, `situation` TEXT NOT NULL, `value` TEXT NOT NULL, `observation` TEXT NOT NULL, `notify` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "service", + "columnName": "service", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "situation", + "columnName": "situation", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "observation", + "columnName": "observation", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notify", + "columnName": "notify", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "service_uniqueness", + "unique": true, + "columnNames": [ + "service", + "date" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `service_uniqueness` ON `${TABLE_NAME}` (`service`, `date`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `imageUrl` TEXT, `username` TEXT NOT NULL, `email` TEXT, `darkThemeEnabled` INTEGER NOT NULL, `darkThemeInvites` INTEGER NOT NULL, `grouping` INTEGER, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "darkThemeEnabled", + "columnName": "darkThemeEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "darkThemeInvites", + "columnName": "darkThemeInvites", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "grouping", + "columnName": "grouping", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "STeacher", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`teacherId` INTEGER NOT NULL, `name` TEXT NOT NULL, `imageUrl` TEXT, PRIMARY KEY(`teacherId`))", + "fields": [ + { + "fieldPath": "teacherId", + "columnName": "teacherId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "teacherId" + ] + }, + "indices": [ + { + "name": "index_STeacher_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_STeacher_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "SDiscipline", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`disciplineId` INTEGER NOT NULL, `department` TEXT NOT NULL, `departmentName` TEXT, `code` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`disciplineId`))", + "fields": [ + { + "fieldPath": "disciplineId", + "columnName": "disciplineId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "department", + "columnName": "department", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "departmentName", + "columnName": "departmentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "disciplineId" + ] + }, + "indices": [ + { + "name": "index_SDiscipline_code_department", + "unique": true, + "columnNames": [ + "code", + "department" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SDiscipline_code_department` ON `${TABLE_NAME}` (`code`, `department`)" + }, + { + "name": "index_SDiscipline_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SDiscipline_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_SDiscipline_code", + "unique": false, + "columnNames": [ + "code" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SDiscipline_code` ON `${TABLE_NAME}` (`code`)" + }, + { + "name": "index_SDiscipline_department", + "unique": false, + "columnNames": [ + "department" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SDiscipline_department` ON `${TABLE_NAME}` (`department`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "SStudent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `userId` INTEGER NOT NULL, `name` TEXT NOT NULL, `imageUrl` TEXT, `course` INTEGER, `courseName` TEXT, `me` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "course", + "columnName": "course", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "courseName", + "columnName": "courseName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "me", + "columnName": "me", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SStudent_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SStudent_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "EvaluationEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `referencedId` INTEGER NOT NULL, `name` TEXT NOT NULL, `extra` TEXT, `image` TEXT, `type` INTEGER NOT NULL, `searchable` TEXT NOT NULL, `comp1` TEXT, `comp2` TEXT, `referenceLong1` INTEGER, `referenceLong2` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referencedId", + "columnName": "referencedId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "extra", + "columnName": "extra", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "comp1", + "columnName": "comp1", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "comp2", + "columnName": "comp2", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "referenceLong1", + "columnName": "referenceLong1", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "referenceLong2", + "columnName": "referenceLong2", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_EvaluationEntity_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EvaluationEntity_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Flowchart", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `courseId` INTEGER NOT NULL, `description` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "courseId", + "columnName": "courseId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastUpdated", + "columnName": "lastUpdated", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "FlowchartSemester", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `flowchartId` INTEGER NOT NULL, `order` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`flowchartId`) REFERENCES `Flowchart`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "flowchartId", + "columnName": "flowchartId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FlowchartSemester_flowchartId", + "unique": false, + "columnNames": [ + "flowchartId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartSemester_flowchartId` ON `${TABLE_NAME}` (`flowchartId`)" + } + ], + "foreignKeys": [ + { + "table": "Flowchart", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "flowchartId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "FlowchartDiscipline", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `disciplineId` INTEGER NOT NULL, `type` TEXT NOT NULL, `mandatory` INTEGER NOT NULL, `semesterId` INTEGER NOT NULL, `completed` INTEGER NOT NULL, `participating` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`disciplineId`) REFERENCES `Discipline`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`semesterId`) REFERENCES `FlowchartSemester`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "disciplineId", + "columnName": "disciplineId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mandatory", + "columnName": "mandatory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semesterId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "completed", + "columnName": "completed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "participating", + "columnName": "participating", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FlowchartDiscipline_disciplineId", + "unique": false, + "columnNames": [ + "disciplineId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartDiscipline_disciplineId` ON `${TABLE_NAME}` (`disciplineId`)" + }, + { + "name": "index_FlowchartDiscipline_semesterId", + "unique": false, + "columnNames": [ + "semesterId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartDiscipline_semesterId` ON `${TABLE_NAME}` (`semesterId`)" + } + ], + "foreignKeys": [ + { + "table": "Discipline", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "disciplineId" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "FlowchartSemester", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "semesterId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "FlowchartRequirement", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `type` TEXT NOT NULL, `disciplineId` INTEGER NOT NULL, `requiredDisciplineId` INTEGER, `coursePercentage` REAL, `courseHours` INTEGER, `typeId` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`disciplineId`) REFERENCES `FlowchartDiscipline`(`id`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`requiredDisciplineId`) REFERENCES `FlowchartDiscipline`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "disciplineId", + "columnName": "disciplineId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "requiredDisciplineId", + "columnName": "requiredDisciplineId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "coursePercentage", + "columnName": "coursePercentage", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "courseHours", + "columnName": "courseHours", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "typeId", + "columnName": "typeId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FlowchartRequirement_disciplineId", + "unique": false, + "columnNames": [ + "disciplineId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartRequirement_disciplineId` ON `${TABLE_NAME}` (`disciplineId`)" + }, + { + "name": "index_FlowchartRequirement_requiredDisciplineId", + "unique": false, + "columnNames": [ + "requiredDisciplineId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartRequirement_requiredDisciplineId` ON `${TABLE_NAME}` (`requiredDisciplineId`)" + } + ], + "foreignKeys": [ + { + "table": "FlowchartDiscipline", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "disciplineId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "FlowchartDiscipline", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "requiredDisciplineId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "ProfileStatement", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `receiverId` INTEGER NOT NULL, `senderId` INTEGER NOT NULL, `senderName` TEXT, `senderPicture` TEXT, `hidden` INTEGER NOT NULL, `text` TEXT NOT NULL, `likes` INTEGER NOT NULL, `approved` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "receiverId", + "columnName": "receiverId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "senderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "senderName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "senderPicture", + "columnName": "senderPicture", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "likes", + "columnName": "likes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "approved", + "columnName": "approved", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "UserSession", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` TEXT NOT NULL, `started` INTEGER NOT NULL, `lastInteraction` INTEGER, `synced` INTEGER NOT NULL, `clickedAd` INTEGER NOT NULL, `impressionAd` INTEGER NOT NULL, PRIMARY KEY(`uid`))", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "started", + "columnName": "started", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastInteraction", + "columnName": "lastInteraction", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "synced", + "columnName": "synced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "clickedAd", + "columnName": "clickedAd", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "impressionAd", + "columnName": "impressionAd", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AffinityQuestion", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `question` TEXT NOT NULL, `answered` INTEGER NOT NULL, `synced` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "question", + "columnName": "question", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "answered", + "columnName": "answered", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "synced", + "columnName": "synced", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AffinityQuestionAlternative", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `question_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, FOREIGN KEY(`question_id`) REFERENCES `AffinityQuestion`(`id`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`student_id`) REFERENCES `SStudent`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "questionId", + "columnName": "question_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_AffinityQuestionAlternative_student_id", + "unique": false, + "columnNames": [ + "student_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AffinityQuestionAlternative_student_id` ON `${TABLE_NAME}` (`student_id`)" + }, + { + "name": "index_AffinityQuestionAlternative_question_id", + "unique": false, + "columnNames": [ + "question_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AffinityQuestionAlternative_question_id` ON `${TABLE_NAME}` (`question_id`)" + } + ], + "foreignKeys": [ + { + "table": "AffinityQuestion", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "question_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "SStudent", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "student_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "Event", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `description` TEXT NOT NULL, `imageUrl` TEXT NOT NULL, `creatorName` TEXT NOT NULL, `creatorId` INTEGER NOT NULL, `offeredBy` TEXT NOT NULL, `startDate` TEXT NOT NULL, `endDate` TEXT NOT NULL, `location` TEXT NOT NULL, `price` REAL, `certificateHours` INTEGER, `courseId` INTEGER, `featured` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `approved` INTEGER NOT NULL, `canModify` INTEGER NOT NULL, `participating` INTEGER NOT NULL, `fakeTemp` INTEGER, `sending` INTEGER, `registerPage` TEXT, `canApprove` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "creatorName", + "columnName": "creatorName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "creatorId", + "columnName": "creatorId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "offeredBy", + "columnName": "offeredBy", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "startDate", + "columnName": "startDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "endDate", + "columnName": "endDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "certificateHours", + "columnName": "certificateHours", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "courseId", + "columnName": "courseId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "featured", + "columnName": "featured", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "approved", + "columnName": "approved", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canModify", + "columnName": "canModify", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "participating", + "columnName": "participating", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fakeTemp", + "columnName": "fakeTemp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sending", + "columnName": "sending", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "registerPage", + "columnName": "registerPage", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "canApprove", + "columnName": "canApprove", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ClassGroupTeacher", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `classGroupId` INTEGER NOT NULL, `teacherId` INTEGER NOT NULL, FOREIGN KEY(`classGroupId`) REFERENCES `ClassGroup`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`teacherId`) REFERENCES `Teacher`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classGroupId", + "columnName": "classGroupId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacherId", + "columnName": "teacherId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassGroupTeacher_classGroupId_teacherId", + "unique": true, + "columnNames": [ + "classGroupId", + "teacherId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassGroupTeacher_classGroupId_teacherId` ON `${TABLE_NAME}` (`classGroupId`, `teacherId`)" + }, + { + "name": "index_ClassGroupTeacher_teacherId", + "unique": false, + "columnNames": [ + "teacherId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassGroupTeacher_teacherId` ON `${TABLE_NAME}` (`teacherId`)" + }, + { + "name": "index_ClassGroupTeacher_classGroupId", + "unique": false, + "columnNames": [ + "classGroupId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassGroupTeacher_classGroupId` ON `${TABLE_NAME}` (`classGroupId`)" + } + ], + "foreignKeys": [ + { + "table": "ClassGroup", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "classGroupId" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Teacher", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "teacherId" + ], + "referencedColumns": [ + "uid" + ] + } + ] + } + ], + "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, '78e31a488be749b13e1092f8ed1f1338')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.forcetower.uefs.core.storage.database.UDatabase/54.json b/app/schemas/com.forcetower.uefs.core.storage.database.UDatabase/54.json new file mode 100644 index 000000000..f27753f72 --- /dev/null +++ b/app/schemas/com.forcetower.uefs.core.storage.database.UDatabase/54.json @@ -0,0 +1,3134 @@ +{ + "formatVersion": 1, + "database": { + "version": 54, + "identityHash": "012d69e2ed1c58002c24f6fb8df753b1", + "entities": [ + { + "tableName": "AccessToken", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` TEXT NOT NULL, `token` TEXT NOT NULL, `refreshToken` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "refreshToken", + "columnName": "refreshToken", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Access", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `valid` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "valid", + "columnName": "valid", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Profile", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `email` TEXT, `score` REAL NOT NULL, `calc_score` REAL NOT NULL, `course` INTEGER, `imageUrl` TEXT, `sagres_id` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `me` INTEGER NOT NULL, `mocked` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "score", + "columnName": "score", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "calcScore", + "columnName": "calc_score", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "course", + "columnName": "course", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sagresId", + "columnName": "sagres_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "me", + "columnName": "me", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mocked", + "columnName": "mocked", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Profile_sagres_id", + "unique": true, + "columnNames": [ + "sagres_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Profile_sagres_id` ON `${TABLE_NAME}` (`sagres_id`)" + }, + { + "name": "index_Profile_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Profile_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semester", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `sagres_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `codename` TEXT NOT NULL, `start` INTEGER, `end` INTEGER, `start_class` INTEGER, `end_class` INTEGER)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sagresId", + "columnName": "sagres_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "codename", + "columnName": "codename", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "startClass", + "columnName": "start_class", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endClass", + "columnName": "end_class", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Semester_sagres_id", + "unique": true, + "columnNames": [ + "sagres_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semester_sagres_id` ON `${TABLE_NAME}` (`sagres_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Message", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `content` TEXT NOT NULL, `sagres_id` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `sender_profile` INTEGER NOT NULL, `sender_name` TEXT, `notified` INTEGER NOT NULL, `discipline` TEXT, `uuid` TEXT NOT NULL, `code_discipline` TEXT, `html` INTEGER NOT NULL, `date_string` TEXT, `processing_time` INTEGER, `hash_message` INTEGER, `attachmentName` TEXT, `attachmentLink` TEXT)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sagresId", + "columnName": "sagres_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderProfile", + "columnName": "sender_profile", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notified", + "columnName": "notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "discipline", + "columnName": "discipline", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "codeDiscipline", + "columnName": "code_discipline", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "html", + "columnName": "html", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateString", + "columnName": "date_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "processingTime", + "columnName": "processing_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hashMessage", + "columnName": "hash_message", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attachmentName", + "columnName": "attachmentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attachmentLink", + "columnName": "attachmentLink", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Message_hash_message", + "unique": true, + "columnNames": [ + "hash_message" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Message_hash_message` ON `${TABLE_NAME}` (`hash_message`)" + }, + { + "name": "index_Message_sagres_id", + "unique": true, + "columnNames": [ + "sagres_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Message_sagres_id` ON `${TABLE_NAME}` (`sagres_id`)" + }, + { + "name": "index_Message_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Message_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "CalendarItem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `message` TEXT NOT NULL, `date` TEXT NOT NULL, `uuid` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_CalendarItem_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_CalendarItem_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Discipline", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `code` TEXT NOT NULL, `credits` INTEGER NOT NULL, `department` TEXT, `resume` TEXT, `short_text` TEXT, `uuid` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "credits", + "columnName": "credits", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "department", + "columnName": "department", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "resume", + "columnName": "resume", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shortText", + "columnName": "short_text", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Discipline_code", + "unique": true, + "columnNames": [ + "code" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Discipline_code` ON `${TABLE_NAME}` (`code`)" + }, + { + "name": "index_Discipline_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Discipline_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Class", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `discipline_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `status` TEXT, `final_score` REAL, `partial_score` REAL, `uuid` TEXT NOT NULL, `missedClasses` INTEGER NOT NULL, `lastClass` TEXT NOT NULL, `nextClass` TEXT NOT NULL, `schedule_only` INTEGER NOT NULL, FOREIGN KEY(`discipline_id`) REFERENCES `Discipline`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`semester_id`) REFERENCES `Semester`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "disciplineId", + "columnName": "discipline_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "finalScore", + "columnName": "final_score", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "partialScore", + "columnName": "partial_score", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "missedClasses", + "columnName": "missedClasses", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastClass", + "columnName": "lastClass", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "nextClass", + "columnName": "nextClass", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scheduleOnly", + "columnName": "schedule_only", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Class_semester_id", + "unique": false, + "columnNames": [ + "semester_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_Class_semester_id` ON `${TABLE_NAME}` (`semester_id`)" + }, + { + "name": "index_Class_discipline_id_semester_id", + "unique": true, + "columnNames": [ + "discipline_id", + "semester_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Class_discipline_id_semester_id` ON `${TABLE_NAME}` (`discipline_id`, `semester_id`)" + }, + { + "name": "index_Class_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Class_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [ + { + "table": "Discipline", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "discipline_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Semester", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "semester_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassGroup", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `class_id` INTEGER NOT NULL, `group` TEXT NOT NULL, `teacher` TEXT, `credits` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `draft` INTEGER NOT NULL, `ignored` INTEGER NOT NULL, `teacher_id` INTEGER, `sagresId` INTEGER, `teacherEmail` TEXT, FOREIGN KEY(`class_id`) REFERENCES `Class`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`teacher_id`) REFERENCES `Teacher`(`uid`) ON UPDATE CASCADE ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "credits", + "columnName": "credits", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "draft", + "columnName": "draft", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ignored", + "columnName": "ignored", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacherId", + "columnName": "teacher_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sagresId", + "columnName": "sagresId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "teacherEmail", + "columnName": "teacherEmail", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassGroup_class_id_group", + "unique": true, + "columnNames": [ + "class_id", + "group" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassGroup_class_id_group` ON `${TABLE_NAME}` (`class_id`, `group`)" + }, + { + "name": "index_ClassGroup_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassGroup_uuid` ON `${TABLE_NAME}` (`uuid`)" + }, + { + "name": "index_ClassGroup_teacher_id", + "unique": false, + "columnNames": [ + "teacher_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassGroup_teacher_id` ON `${TABLE_NAME}` (`teacher_id`)" + } + ], + "foreignKeys": [ + { + "table": "Class", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "class_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Teacher", + "onDelete": "SET NULL", + "onUpdate": "CASCADE", + "columns": [ + "teacher_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassAbsence", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `class_id` INTEGER NOT NULL, `profile_id` INTEGER NOT NULL, `sequence` INTEGER NOT NULL, `description` TEXT NOT NULL, `date` TEXT NOT NULL, `grouping` TEXT NOT NULL, `uuid` TEXT NOT NULL, `notified` INTEGER NOT NULL, FOREIGN KEY(`class_id`) REFERENCES `Class`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`profile_id`) REFERENCES `Profile`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "profileId", + "columnName": "profile_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sequence", + "columnName": "sequence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grouping", + "columnName": "grouping", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notified", + "columnName": "notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassAbsence_profile_id", + "unique": false, + "columnNames": [ + "profile_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassAbsence_profile_id` ON `${TABLE_NAME}` (`profile_id`)" + }, + { + "name": "index_ClassAbsence_class_id", + "unique": false, + "columnNames": [ + "class_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassAbsence_class_id` ON `${TABLE_NAME}` (`class_id`)" + }, + { + "name": "index_ClassAbsence_class_id_profile_id_sequence_grouping", + "unique": true, + "columnNames": [ + "class_id", + "profile_id", + "sequence", + "grouping" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassAbsence_class_id_profile_id_sequence_grouping` ON `${TABLE_NAME}` (`class_id`, `profile_id`, `sequence`, `grouping`)" + }, + { + "name": "index_ClassAbsence_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassAbsence_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [ + { + "table": "Class", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "class_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Profile", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "profile_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassLocation", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `group_id` INTEGER NOT NULL, `profile_id` INTEGER NOT NULL, `starts_at` TEXT NOT NULL, `ends_at` TEXT NOT NULL, `day` TEXT NOT NULL, `room` TEXT, `modulo` TEXT, `campus` TEXT, `uuid` TEXT NOT NULL, `hidden_on_schedule` INTEGER NOT NULL, `startsAtInt` INTEGER NOT NULL, `endsAtInt` INTEGER NOT NULL, `dayInt` INTEGER NOT NULL, FOREIGN KEY(`group_id`) REFERENCES `ClassGroup`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`profile_id`) REFERENCES `Profile`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupId", + "columnName": "group_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "profileId", + "columnName": "profile_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "startsAt", + "columnName": "starts_at", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "endsAt", + "columnName": "ends_at", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "day", + "columnName": "day", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "modulo", + "columnName": "modulo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "campus", + "columnName": "campus", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hiddenOnSchedule", + "columnName": "hidden_on_schedule", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "startsAtInt", + "columnName": "startsAtInt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endsAtInt", + "columnName": "endsAtInt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dayInt", + "columnName": "dayInt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassLocation_group_id_day_starts_at_ends_at_profile_id", + "unique": true, + "columnNames": [ + "group_id", + "day", + "starts_at", + "ends_at", + "profile_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassLocation_group_id_day_starts_at_ends_at_profile_id` ON `${TABLE_NAME}` (`group_id`, `day`, `starts_at`, `ends_at`, `profile_id`)" + }, + { + "name": "index_ClassLocation_profile_id", + "unique": false, + "columnNames": [ + "profile_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassLocation_profile_id` ON `${TABLE_NAME}` (`profile_id`)" + }, + { + "name": "index_ClassLocation_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassLocation_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [ + { + "table": "ClassGroup", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "group_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Profile", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "profile_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassItem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `group_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `situation` TEXT, `subject` TEXT, `date` TEXT, `number_of_materials` INTEGER NOT NULL, `material_links` TEXT NOT NULL, `is_new` INTEGER NOT NULL, `uuid` TEXT NOT NULL, FOREIGN KEY(`group_id`) REFERENCES `ClassGroup`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupId", + "columnName": "group_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "situation", + "columnName": "situation", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "numberOfMaterials", + "columnName": "number_of_materials", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "materialLinks", + "columnName": "material_links", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isNew", + "columnName": "is_new", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassItem_group_id_number", + "unique": true, + "columnNames": [ + "group_id", + "number" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassItem_group_id_number` ON `${TABLE_NAME}` (`group_id`, `number`)" + }, + { + "name": "index_ClassItem_number_of_materials", + "unique": false, + "columnNames": [ + "number_of_materials" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassItem_number_of_materials` ON `${TABLE_NAME}` (`number_of_materials`)" + }, + { + "name": "index_ClassItem_situation", + "unique": false, + "columnNames": [ + "situation" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassItem_situation` ON `${TABLE_NAME}` (`situation`)" + }, + { + "name": "index_ClassItem_date", + "unique": false, + "columnNames": [ + "date" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassItem_date` ON `${TABLE_NAME}` (`date`)" + }, + { + "name": "index_ClassItem_is_new", + "unique": false, + "columnNames": [ + "is_new" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassItem_is_new` ON `${TABLE_NAME}` (`is_new`)" + }, + { + "name": "index_ClassItem_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassItem_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [ + { + "table": "ClassGroup", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "group_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassMaterial", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `group_id` INTEGER NOT NULL, `class_item_id` INTEGER, `name` TEXT NOT NULL, `link` TEXT NOT NULL, `is_new` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `notified` INTEGER NOT NULL, FOREIGN KEY(`group_id`) REFERENCES `ClassGroup`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`class_item_id`) REFERENCES `ClassItem`(`uid`) ON UPDATE CASCADE ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupId", + "columnName": "group_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classItemId", + "columnName": "class_item_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isNew", + "columnName": "is_new", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notified", + "columnName": "notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassMaterial_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_ClassMaterial_link", + "unique": false, + "columnNames": [ + "link" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_link` ON `${TABLE_NAME}` (`link`)" + }, + { + "name": "index_ClassMaterial_group_id", + "unique": false, + "columnNames": [ + "group_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_group_id` ON `${TABLE_NAME}` (`group_id`)" + }, + { + "name": "index_ClassMaterial_class_item_id", + "unique": false, + "columnNames": [ + "class_item_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_class_item_id` ON `${TABLE_NAME}` (`class_item_id`)" + }, + { + "name": "index_ClassMaterial_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassMaterial_uuid` ON `${TABLE_NAME}` (`uuid`)" + }, + { + "name": "index_ClassMaterial_is_new", + "unique": false, + "columnNames": [ + "is_new" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_is_new` ON `${TABLE_NAME}` (`is_new`)" + }, + { + "name": "index_ClassMaterial_name_link_group_id", + "unique": true, + "columnNames": [ + "name", + "link", + "group_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassMaterial_name_link_group_id` ON `${TABLE_NAME}` (`name`, `link`, `group_id`)" + } + ], + "foreignKeys": [ + { + "table": "ClassGroup", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "group_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "ClassItem", + "onDelete": "SET NULL", + "onUpdate": "CASCADE", + "columns": [ + "class_item_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "Grade", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` TEXT, `grade` TEXT, `grouping` INTEGER NOT NULL, `groupingName` TEXT NOT NULL, `notified` INTEGER NOT NULL, FOREIGN KEY(`class_id`) REFERENCES `Class`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "grouping", + "columnName": "grouping", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupingName", + "columnName": "groupingName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notified", + "columnName": "notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Grade_class_id", + "unique": false, + "columnNames": [ + "class_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_Grade_class_id` ON `${TABLE_NAME}` (`class_id`)" + }, + { + "name": "index_Grade_name_class_id_grouping", + "unique": true, + "columnNames": [ + "name", + "class_id", + "grouping" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Grade_name_class_id_grouping` ON `${TABLE_NAME}` (`name`, `class_id`, `grouping`)" + } + ], + "foreignKeys": [ + { + "table": "Class", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "class_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "Course", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `image` TEXT, `since` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "since", + "columnName": "since", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SagresDocument", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` TEXT NOT NULL, `name` TEXT NOT NULL, `downloaded` INTEGER NOT NULL, `downloading` INTEGER NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "downloaded", + "columnName": "downloaded", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "downloading", + "columnName": "downloading", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_SagresDocument_type", + "unique": true, + "columnNames": [ + "type" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SagresDocument_type` ON `${TABLE_NAME}` (`type`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "SyncRegistry", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uuid` TEXT NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER, `completed` INTEGER NOT NULL, `success` INTEGER NOT NULL, `error` INTEGER NOT NULL, `executor` TEXT NOT NULL, `message` TEXT NOT NULL, `networkType` INTEGER NOT NULL, `network` TEXT NOT NULL, `skipped` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "completed", + "columnName": "completed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "success", + "columnName": "success", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "executor", + "columnName": "executor", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "networkType", + "columnName": "networkType", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "network", + "columnName": "network", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "skipped", + "columnName": "skipped", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_SyncRegistry_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SyncRegistry_uuid` ON `${TABLE_NAME}` (`uuid`)" + }, + { + "name": "index_SyncRegistry_start", + "unique": true, + "columnNames": [ + "start" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SyncRegistry_start` ON `${TABLE_NAME}` (`start`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Teacher", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `email` TEXT, `sagresId` INTEGER, `department` TEXT, `uuid` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sagresId", + "columnName": "sagresId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "department", + "columnName": "department", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Teacher_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Teacher_uuid` ON `${TABLE_NAME}` (`uuid`)" + }, + { + "name": "index_Teacher_sagresId", + "unique": true, + "columnNames": [ + "sagresId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Teacher_sagresId` ON `${TABLE_NAME}` (`sagresId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "SDemandOffer", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `code` TEXT NOT NULL, `name` TEXT NOT NULL, `selected` INTEGER NOT NULL, `category` TEXT NOT NULL, `hours` INTEGER NOT NULL, `completed` INTEGER NOT NULL, `available` INTEGER NOT NULL, `current` INTEGER NOT NULL, `selectable` INTEGER NOT NULL, `unavailable` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "selected", + "columnName": "selected", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hours", + "columnName": "hours", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "completed", + "columnName": "completed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "available", + "columnName": "available", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "selectable", + "columnName": "selectable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unavailable", + "columnName": "unavailable", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SagresFlags", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `demand_open` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "demandOpen", + "columnName": "demand_open", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Contributor", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `login` TEXT NOT NULL, `total` INTEGER NOT NULL, `name` TEXT NOT NULL, `image` TEXT, `link` TEXT, `url` TEXT, `bio` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "login", + "columnName": "login", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "total", + "columnName": "total", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "bio", + "columnName": "bio", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_Contributor_login", + "unique": true, + "columnNames": [ + "login" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Contributor_login` ON `${TABLE_NAME}` (`login`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "ServiceRequest", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service` TEXT NOT NULL, `date` TEXT NOT NULL, `amount` INTEGER NOT NULL, `situation` TEXT NOT NULL, `value` TEXT NOT NULL, `observation` TEXT NOT NULL, `notify` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "service", + "columnName": "service", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "situation", + "columnName": "situation", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "observation", + "columnName": "observation", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notify", + "columnName": "notify", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "service_uniqueness", + "unique": true, + "columnNames": [ + "service", + "date" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `service_uniqueness` ON `${TABLE_NAME}` (`service`, `date`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `imageUrl` TEXT, `username` TEXT NOT NULL, `email` TEXT, `darkThemeEnabled` INTEGER NOT NULL, `darkThemeInvites` INTEGER NOT NULL, `grouping` INTEGER, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "darkThemeEnabled", + "columnName": "darkThemeEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "darkThemeInvites", + "columnName": "darkThemeInvites", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "grouping", + "columnName": "grouping", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "STeacher", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`teacherId` INTEGER NOT NULL, `name` TEXT NOT NULL, `imageUrl` TEXT, PRIMARY KEY(`teacherId`))", + "fields": [ + { + "fieldPath": "teacherId", + "columnName": "teacherId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "teacherId" + ] + }, + "indices": [ + { + "name": "index_STeacher_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_STeacher_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "SDiscipline", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`disciplineId` INTEGER NOT NULL, `department` TEXT NOT NULL, `departmentName` TEXT, `code` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`disciplineId`))", + "fields": [ + { + "fieldPath": "disciplineId", + "columnName": "disciplineId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "department", + "columnName": "department", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "departmentName", + "columnName": "departmentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "disciplineId" + ] + }, + "indices": [ + { + "name": "index_SDiscipline_code_department", + "unique": true, + "columnNames": [ + "code", + "department" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SDiscipline_code_department` ON `${TABLE_NAME}` (`code`, `department`)" + }, + { + "name": "index_SDiscipline_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SDiscipline_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_SDiscipline_code", + "unique": false, + "columnNames": [ + "code" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SDiscipline_code` ON `${TABLE_NAME}` (`code`)" + }, + { + "name": "index_SDiscipline_department", + "unique": false, + "columnNames": [ + "department" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SDiscipline_department` ON `${TABLE_NAME}` (`department`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "SStudent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `userId` INTEGER NOT NULL, `name` TEXT NOT NULL, `imageUrl` TEXT, `course` INTEGER, `courseName` TEXT, `me` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "course", + "columnName": "course", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "courseName", + "columnName": "courseName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "me", + "columnName": "me", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SStudent_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SStudent_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "EvaluationEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `referencedId` INTEGER NOT NULL, `name` TEXT NOT NULL, `extra` TEXT, `image` TEXT, `type` INTEGER NOT NULL, `searchable` TEXT NOT NULL, `comp1` TEXT, `comp2` TEXT, `referenceLong1` INTEGER, `referenceLong2` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referencedId", + "columnName": "referencedId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "extra", + "columnName": "extra", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "comp1", + "columnName": "comp1", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "comp2", + "columnName": "comp2", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "referenceLong1", + "columnName": "referenceLong1", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "referenceLong2", + "columnName": "referenceLong2", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_EvaluationEntity_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EvaluationEntity_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Flowchart", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `courseId` INTEGER NOT NULL, `description` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "courseId", + "columnName": "courseId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastUpdated", + "columnName": "lastUpdated", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "FlowchartSemester", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `flowchartId` INTEGER NOT NULL, `order` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`flowchartId`) REFERENCES `Flowchart`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "flowchartId", + "columnName": "flowchartId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FlowchartSemester_flowchartId", + "unique": false, + "columnNames": [ + "flowchartId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartSemester_flowchartId` ON `${TABLE_NAME}` (`flowchartId`)" + } + ], + "foreignKeys": [ + { + "table": "Flowchart", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "flowchartId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "FlowchartDiscipline", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `disciplineId` INTEGER NOT NULL, `type` TEXT NOT NULL, `mandatory` INTEGER NOT NULL, `semesterId` INTEGER NOT NULL, `completed` INTEGER NOT NULL, `participating` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`disciplineId`) REFERENCES `Discipline`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`semesterId`) REFERENCES `FlowchartSemester`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "disciplineId", + "columnName": "disciplineId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mandatory", + "columnName": "mandatory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semesterId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "completed", + "columnName": "completed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "participating", + "columnName": "participating", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FlowchartDiscipline_disciplineId", + "unique": false, + "columnNames": [ + "disciplineId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartDiscipline_disciplineId` ON `${TABLE_NAME}` (`disciplineId`)" + }, + { + "name": "index_FlowchartDiscipline_semesterId", + "unique": false, + "columnNames": [ + "semesterId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartDiscipline_semesterId` ON `${TABLE_NAME}` (`semesterId`)" + } + ], + "foreignKeys": [ + { + "table": "Discipline", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "disciplineId" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "FlowchartSemester", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "semesterId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "FlowchartRequirement", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `type` TEXT NOT NULL, `disciplineId` INTEGER NOT NULL, `requiredDisciplineId` INTEGER, `coursePercentage` REAL, `courseHours` INTEGER, `typeId` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`disciplineId`) REFERENCES `FlowchartDiscipline`(`id`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`requiredDisciplineId`) REFERENCES `FlowchartDiscipline`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "disciplineId", + "columnName": "disciplineId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "requiredDisciplineId", + "columnName": "requiredDisciplineId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "coursePercentage", + "columnName": "coursePercentage", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "courseHours", + "columnName": "courseHours", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "typeId", + "columnName": "typeId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FlowchartRequirement_disciplineId", + "unique": false, + "columnNames": [ + "disciplineId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartRequirement_disciplineId` ON `${TABLE_NAME}` (`disciplineId`)" + }, + { + "name": "index_FlowchartRequirement_requiredDisciplineId", + "unique": false, + "columnNames": [ + "requiredDisciplineId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartRequirement_requiredDisciplineId` ON `${TABLE_NAME}` (`requiredDisciplineId`)" + } + ], + "foreignKeys": [ + { + "table": "FlowchartDiscipline", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "disciplineId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "FlowchartDiscipline", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "requiredDisciplineId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "ProfileStatement", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `receiverId` INTEGER NOT NULL, `senderId` INTEGER NOT NULL, `senderName` TEXT, `senderPicture` TEXT, `hidden` INTEGER NOT NULL, `text` TEXT NOT NULL, `likes` INTEGER NOT NULL, `approved` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "receiverId", + "columnName": "receiverId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "senderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "senderName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "senderPicture", + "columnName": "senderPicture", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "likes", + "columnName": "likes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "approved", + "columnName": "approved", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "UserSession", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` TEXT NOT NULL, `started` INTEGER NOT NULL, `lastInteraction` INTEGER, `synced` INTEGER NOT NULL, `clickedAd` INTEGER NOT NULL, `impressionAd` INTEGER NOT NULL, PRIMARY KEY(`uid`))", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "started", + "columnName": "started", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastInteraction", + "columnName": "lastInteraction", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "synced", + "columnName": "synced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "clickedAd", + "columnName": "clickedAd", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "impressionAd", + "columnName": "impressionAd", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AffinityQuestion", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `question` TEXT NOT NULL, `answered` INTEGER NOT NULL, `synced` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "question", + "columnName": "question", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "answered", + "columnName": "answered", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "synced", + "columnName": "synced", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AffinityQuestionAlternative", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `question_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, FOREIGN KEY(`question_id`) REFERENCES `AffinityQuestion`(`id`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`student_id`) REFERENCES `SStudent`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "questionId", + "columnName": "question_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_AffinityQuestionAlternative_student_id", + "unique": false, + "columnNames": [ + "student_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AffinityQuestionAlternative_student_id` ON `${TABLE_NAME}` (`student_id`)" + }, + { + "name": "index_AffinityQuestionAlternative_question_id", + "unique": false, + "columnNames": [ + "question_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AffinityQuestionAlternative_question_id` ON `${TABLE_NAME}` (`question_id`)" + } + ], + "foreignKeys": [ + { + "table": "AffinityQuestion", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "question_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "SStudent", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "student_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "Event", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `description` TEXT NOT NULL, `imageUrl` TEXT NOT NULL, `creatorName` TEXT NOT NULL, `creatorId` INTEGER NOT NULL, `offeredBy` TEXT NOT NULL, `startDate` TEXT NOT NULL, `endDate` TEXT NOT NULL, `location` TEXT NOT NULL, `price` REAL, `certificateHours` INTEGER, `courseId` INTEGER, `featured` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `approved` INTEGER NOT NULL, `canModify` INTEGER NOT NULL, `participating` INTEGER NOT NULL, `fakeTemp` INTEGER, `sending` INTEGER, `registerPage` TEXT, `canApprove` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "creatorName", + "columnName": "creatorName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "creatorId", + "columnName": "creatorId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "offeredBy", + "columnName": "offeredBy", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "startDate", + "columnName": "startDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "endDate", + "columnName": "endDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "certificateHours", + "columnName": "certificateHours", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "courseId", + "columnName": "courseId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "featured", + "columnName": "featured", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "approved", + "columnName": "approved", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canModify", + "columnName": "canModify", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "participating", + "columnName": "participating", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fakeTemp", + "columnName": "fakeTemp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sending", + "columnName": "sending", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "registerPage", + "columnName": "registerPage", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "canApprove", + "columnName": "canApprove", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ClassGroupTeacher", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `classGroupId` INTEGER NOT NULL, `teacherId` INTEGER NOT NULL, FOREIGN KEY(`classGroupId`) REFERENCES `ClassGroup`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`teacherId`) REFERENCES `Teacher`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classGroupId", + "columnName": "classGroupId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacherId", + "columnName": "teacherId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassGroupTeacher_classGroupId_teacherId", + "unique": true, + "columnNames": [ + "classGroupId", + "teacherId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassGroupTeacher_classGroupId_teacherId` ON `${TABLE_NAME}` (`classGroupId`, `teacherId`)" + }, + { + "name": "index_ClassGroupTeacher_teacherId", + "unique": false, + "columnNames": [ + "teacherId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassGroupTeacher_teacherId` ON `${TABLE_NAME}` (`teacherId`)" + }, + { + "name": "index_ClassGroupTeacher_classGroupId", + "unique": false, + "columnNames": [ + "classGroupId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassGroupTeacher_classGroupId` ON `${TABLE_NAME}` (`classGroupId`)" + } + ], + "foreignKeys": [ + { + "table": "ClassGroup", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "classGroupId" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Teacher", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "teacherId" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "EdgeAccessToken", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accessToken` TEXT NOT NULL, `id` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "accessToken", + "columnName": "accessToken", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "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, '012d69e2ed1c58002c24f6fb8df753b1')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.forcetower.uefs.core.storage.database.UDatabase/55.json b/app/schemas/com.forcetower.uefs.core.storage.database.UDatabase/55.json new file mode 100644 index 000000000..8126f0352 --- /dev/null +++ b/app/schemas/com.forcetower.uefs.core.storage.database.UDatabase/55.json @@ -0,0 +1,3178 @@ +{ + "formatVersion": 1, + "database": { + "version": 55, + "identityHash": "243bd94a71ae533546d4d1081f5ad83e", + "entities": [ + { + "tableName": "AccessToken", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` TEXT NOT NULL, `token` TEXT NOT NULL, `refreshToken` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "refreshToken", + "columnName": "refreshToken", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Access", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `valid` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "valid", + "columnName": "valid", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Profile", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `email` TEXT, `score` REAL NOT NULL, `calc_score` REAL NOT NULL, `course` INTEGER, `imageUrl` TEXT, `sagres_id` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `me` INTEGER NOT NULL, `mocked` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "score", + "columnName": "score", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "calcScore", + "columnName": "calc_score", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "course", + "columnName": "course", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sagresId", + "columnName": "sagres_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "me", + "columnName": "me", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mocked", + "columnName": "mocked", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Profile_sagres_id", + "unique": true, + "columnNames": [ + "sagres_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Profile_sagres_id` ON `${TABLE_NAME}` (`sagres_id`)" + }, + { + "name": "index_Profile_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Profile_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semester", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `sagres_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `codename` TEXT NOT NULL, `start` INTEGER, `end` INTEGER, `start_class` INTEGER, `end_class` INTEGER)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sagresId", + "columnName": "sagres_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "codename", + "columnName": "codename", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "startClass", + "columnName": "start_class", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endClass", + "columnName": "end_class", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Semester_sagres_id", + "unique": true, + "columnNames": [ + "sagres_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semester_sagres_id` ON `${TABLE_NAME}` (`sagres_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Message", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `content` TEXT NOT NULL, `sagres_id` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `sender_profile` INTEGER NOT NULL, `sender_name` TEXT, `notified` INTEGER NOT NULL, `discipline` TEXT, `uuid` TEXT NOT NULL, `code_discipline` TEXT, `html` INTEGER NOT NULL, `date_string` TEXT, `processing_time` INTEGER, `hash_message` INTEGER, `attachmentName` TEXT, `attachmentLink` TEXT)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sagresId", + "columnName": "sagres_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderProfile", + "columnName": "sender_profile", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notified", + "columnName": "notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "discipline", + "columnName": "discipline", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "codeDiscipline", + "columnName": "code_discipline", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "html", + "columnName": "html", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateString", + "columnName": "date_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "processingTime", + "columnName": "processing_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hashMessage", + "columnName": "hash_message", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attachmentName", + "columnName": "attachmentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attachmentLink", + "columnName": "attachmentLink", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Message_hash_message", + "unique": true, + "columnNames": [ + "hash_message" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Message_hash_message` ON `${TABLE_NAME}` (`hash_message`)" + }, + { + "name": "index_Message_sagres_id", + "unique": true, + "columnNames": [ + "sagres_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Message_sagres_id` ON `${TABLE_NAME}` (`sagres_id`)" + }, + { + "name": "index_Message_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Message_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "CalendarItem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `message` TEXT NOT NULL, `date` TEXT NOT NULL, `uuid` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_CalendarItem_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_CalendarItem_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Discipline", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `code` TEXT NOT NULL, `credits` INTEGER NOT NULL, `department` TEXT, `resume` TEXT, `short_text` TEXT, `uuid` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "credits", + "columnName": "credits", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "department", + "columnName": "department", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "resume", + "columnName": "resume", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shortText", + "columnName": "short_text", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Discipline_code", + "unique": true, + "columnNames": [ + "code" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Discipline_code` ON `${TABLE_NAME}` (`code`)" + }, + { + "name": "index_Discipline_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Discipline_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Class", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `discipline_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `status` TEXT, `final_score` REAL, `partial_score` REAL, `uuid` TEXT NOT NULL, `missedClasses` INTEGER NOT NULL, `lastClass` TEXT NOT NULL, `nextClass` TEXT NOT NULL, `schedule_only` INTEGER NOT NULL, FOREIGN KEY(`discipline_id`) REFERENCES `Discipline`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`semester_id`) REFERENCES `Semester`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "disciplineId", + "columnName": "discipline_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "finalScore", + "columnName": "final_score", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "partialScore", + "columnName": "partial_score", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "missedClasses", + "columnName": "missedClasses", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastClass", + "columnName": "lastClass", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "nextClass", + "columnName": "nextClass", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "scheduleOnly", + "columnName": "schedule_only", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Class_semester_id", + "unique": false, + "columnNames": [ + "semester_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_Class_semester_id` ON `${TABLE_NAME}` (`semester_id`)" + }, + { + "name": "index_Class_discipline_id_semester_id", + "unique": true, + "columnNames": [ + "discipline_id", + "semester_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Class_discipline_id_semester_id` ON `${TABLE_NAME}` (`discipline_id`, `semester_id`)" + }, + { + "name": "index_Class_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Class_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [ + { + "table": "Discipline", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "discipline_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Semester", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "semester_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassGroup", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `class_id` INTEGER NOT NULL, `group` TEXT NOT NULL, `teacher` TEXT, `credits` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `draft` INTEGER NOT NULL, `ignored` INTEGER NOT NULL, `teacher_id` INTEGER, `sagresId` INTEGER, `teacherEmail` TEXT, FOREIGN KEY(`class_id`) REFERENCES `Class`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`teacher_id`) REFERENCES `Teacher`(`uid`) ON UPDATE CASCADE ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "credits", + "columnName": "credits", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "draft", + "columnName": "draft", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ignored", + "columnName": "ignored", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacherId", + "columnName": "teacher_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sagresId", + "columnName": "sagresId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "teacherEmail", + "columnName": "teacherEmail", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassGroup_class_id_group", + "unique": true, + "columnNames": [ + "class_id", + "group" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassGroup_class_id_group` ON `${TABLE_NAME}` (`class_id`, `group`)" + }, + { + "name": "index_ClassGroup_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassGroup_uuid` ON `${TABLE_NAME}` (`uuid`)" + }, + { + "name": "index_ClassGroup_teacher_id", + "unique": false, + "columnNames": [ + "teacher_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassGroup_teacher_id` ON `${TABLE_NAME}` (`teacher_id`)" + } + ], + "foreignKeys": [ + { + "table": "Class", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "class_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Teacher", + "onDelete": "SET NULL", + "onUpdate": "CASCADE", + "columns": [ + "teacher_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassAbsence", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `class_id` INTEGER NOT NULL, `profile_id` INTEGER NOT NULL, `sequence` INTEGER NOT NULL, `description` TEXT NOT NULL, `date` TEXT NOT NULL, `grouping` TEXT NOT NULL, `uuid` TEXT NOT NULL, `notified` INTEGER NOT NULL, FOREIGN KEY(`class_id`) REFERENCES `Class`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`profile_id`) REFERENCES `Profile`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "profileId", + "columnName": "profile_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sequence", + "columnName": "sequence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grouping", + "columnName": "grouping", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notified", + "columnName": "notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassAbsence_profile_id", + "unique": false, + "columnNames": [ + "profile_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassAbsence_profile_id` ON `${TABLE_NAME}` (`profile_id`)" + }, + { + "name": "index_ClassAbsence_class_id", + "unique": false, + "columnNames": [ + "class_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassAbsence_class_id` ON `${TABLE_NAME}` (`class_id`)" + }, + { + "name": "index_ClassAbsence_class_id_profile_id_sequence_grouping", + "unique": true, + "columnNames": [ + "class_id", + "profile_id", + "sequence", + "grouping" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassAbsence_class_id_profile_id_sequence_grouping` ON `${TABLE_NAME}` (`class_id`, `profile_id`, `sequence`, `grouping`)" + }, + { + "name": "index_ClassAbsence_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassAbsence_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [ + { + "table": "Class", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "class_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Profile", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "profile_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassLocation", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `group_id` INTEGER NOT NULL, `profile_id` INTEGER NOT NULL, `starts_at` TEXT NOT NULL, `ends_at` TEXT NOT NULL, `day` TEXT NOT NULL, `room` TEXT, `modulo` TEXT, `campus` TEXT, `uuid` TEXT NOT NULL, `hidden_on_schedule` INTEGER NOT NULL, `startsAtInt` INTEGER NOT NULL, `endsAtInt` INTEGER NOT NULL, `dayInt` INTEGER NOT NULL, FOREIGN KEY(`group_id`) REFERENCES `ClassGroup`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`profile_id`) REFERENCES `Profile`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupId", + "columnName": "group_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "profileId", + "columnName": "profile_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "startsAt", + "columnName": "starts_at", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "endsAt", + "columnName": "ends_at", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "day", + "columnName": "day", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "modulo", + "columnName": "modulo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "campus", + "columnName": "campus", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hiddenOnSchedule", + "columnName": "hidden_on_schedule", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "startsAtInt", + "columnName": "startsAtInt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endsAtInt", + "columnName": "endsAtInt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dayInt", + "columnName": "dayInt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassLocation_group_id_day_starts_at_ends_at_profile_id", + "unique": true, + "columnNames": [ + "group_id", + "day", + "starts_at", + "ends_at", + "profile_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassLocation_group_id_day_starts_at_ends_at_profile_id` ON `${TABLE_NAME}` (`group_id`, `day`, `starts_at`, `ends_at`, `profile_id`)" + }, + { + "name": "index_ClassLocation_profile_id", + "unique": false, + "columnNames": [ + "profile_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassLocation_profile_id` ON `${TABLE_NAME}` (`profile_id`)" + }, + { + "name": "index_ClassLocation_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassLocation_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [ + { + "table": "ClassGroup", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "group_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Profile", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "profile_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassItem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `group_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `situation` TEXT, `subject` TEXT, `date` TEXT, `number_of_materials` INTEGER NOT NULL, `material_links` TEXT NOT NULL, `is_new` INTEGER NOT NULL, `uuid` TEXT NOT NULL, FOREIGN KEY(`group_id`) REFERENCES `ClassGroup`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupId", + "columnName": "group_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "situation", + "columnName": "situation", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "numberOfMaterials", + "columnName": "number_of_materials", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "materialLinks", + "columnName": "material_links", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isNew", + "columnName": "is_new", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassItem_group_id_number", + "unique": true, + "columnNames": [ + "group_id", + "number" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassItem_group_id_number` ON `${TABLE_NAME}` (`group_id`, `number`)" + }, + { + "name": "index_ClassItem_number_of_materials", + "unique": false, + "columnNames": [ + "number_of_materials" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassItem_number_of_materials` ON `${TABLE_NAME}` (`number_of_materials`)" + }, + { + "name": "index_ClassItem_situation", + "unique": false, + "columnNames": [ + "situation" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassItem_situation` ON `${TABLE_NAME}` (`situation`)" + }, + { + "name": "index_ClassItem_date", + "unique": false, + "columnNames": [ + "date" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassItem_date` ON `${TABLE_NAME}` (`date`)" + }, + { + "name": "index_ClassItem_is_new", + "unique": false, + "columnNames": [ + "is_new" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassItem_is_new` ON `${TABLE_NAME}` (`is_new`)" + }, + { + "name": "index_ClassItem_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassItem_uuid` ON `${TABLE_NAME}` (`uuid`)" + } + ], + "foreignKeys": [ + { + "table": "ClassGroup", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "group_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "ClassMaterial", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `group_id` INTEGER NOT NULL, `class_item_id` INTEGER, `name` TEXT NOT NULL, `link` TEXT NOT NULL, `is_new` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `notified` INTEGER NOT NULL, FOREIGN KEY(`group_id`) REFERENCES `ClassGroup`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`class_item_id`) REFERENCES `ClassItem`(`uid`) ON UPDATE CASCADE ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupId", + "columnName": "group_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classItemId", + "columnName": "class_item_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isNew", + "columnName": "is_new", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notified", + "columnName": "notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassMaterial_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_ClassMaterial_link", + "unique": false, + "columnNames": [ + "link" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_link` ON `${TABLE_NAME}` (`link`)" + }, + { + "name": "index_ClassMaterial_group_id", + "unique": false, + "columnNames": [ + "group_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_group_id` ON `${TABLE_NAME}` (`group_id`)" + }, + { + "name": "index_ClassMaterial_class_item_id", + "unique": false, + "columnNames": [ + "class_item_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_class_item_id` ON `${TABLE_NAME}` (`class_item_id`)" + }, + { + "name": "index_ClassMaterial_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassMaterial_uuid` ON `${TABLE_NAME}` (`uuid`)" + }, + { + "name": "index_ClassMaterial_is_new", + "unique": false, + "columnNames": [ + "is_new" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassMaterial_is_new` ON `${TABLE_NAME}` (`is_new`)" + }, + { + "name": "index_ClassMaterial_name_link_group_id", + "unique": true, + "columnNames": [ + "name", + "link", + "group_id" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassMaterial_name_link_group_id` ON `${TABLE_NAME}` (`name`, `link`, `group_id`)" + } + ], + "foreignKeys": [ + { + "table": "ClassGroup", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "group_id" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "ClassItem", + "onDelete": "SET NULL", + "onUpdate": "CASCADE", + "columns": [ + "class_item_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "Grade", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` TEXT, `grade` TEXT, `grouping` INTEGER NOT NULL, `groupingName` TEXT NOT NULL, `notified` INTEGER NOT NULL, FOREIGN KEY(`class_id`) REFERENCES `Class`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "grouping", + "columnName": "grouping", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupingName", + "columnName": "groupingName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notified", + "columnName": "notified", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Grade_class_id", + "unique": false, + "columnNames": [ + "class_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_Grade_class_id` ON `${TABLE_NAME}` (`class_id`)" + }, + { + "name": "index_Grade_name_class_id_grouping", + "unique": true, + "columnNames": [ + "name", + "class_id", + "grouping" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Grade_name_class_id_grouping` ON `${TABLE_NAME}` (`name`, `class_id`, `grouping`)" + } + ], + "foreignKeys": [ + { + "table": "Class", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "class_id" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "Course", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `image` TEXT, `since` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "since", + "columnName": "since", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SagresDocument", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` TEXT NOT NULL, `name` TEXT NOT NULL, `downloaded` INTEGER NOT NULL, `downloading` INTEGER NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "downloaded", + "columnName": "downloaded", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "downloading", + "columnName": "downloading", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_SagresDocument_type", + "unique": true, + "columnNames": [ + "type" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SagresDocument_type` ON `${TABLE_NAME}` (`type`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "SyncRegistry", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uuid` TEXT NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER, `completed` INTEGER NOT NULL, `success` INTEGER NOT NULL, `error` INTEGER NOT NULL, `executor` TEXT NOT NULL, `message` TEXT NOT NULL, `networkType` INTEGER NOT NULL, `network` TEXT NOT NULL, `skipped` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "completed", + "columnName": "completed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "success", + "columnName": "success", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "error", + "columnName": "error", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "executor", + "columnName": "executor", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "networkType", + "columnName": "networkType", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "network", + "columnName": "network", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "skipped", + "columnName": "skipped", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_SyncRegistry_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SyncRegistry_uuid` ON `${TABLE_NAME}` (`uuid`)" + }, + { + "name": "index_SyncRegistry_start", + "unique": true, + "columnNames": [ + "start" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SyncRegistry_start` ON `${TABLE_NAME}` (`start`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Teacher", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `email` TEXT, `sagresId` INTEGER, `department` TEXT, `uuid` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sagresId", + "columnName": "sagresId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "department", + "columnName": "department", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_Teacher_uuid", + "unique": true, + "columnNames": [ + "uuid" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Teacher_uuid` ON `${TABLE_NAME}` (`uuid`)" + }, + { + "name": "index_Teacher_sagresId", + "unique": true, + "columnNames": [ + "sagresId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Teacher_sagresId` ON `${TABLE_NAME}` (`sagresId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "SDemandOffer", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `code` TEXT NOT NULL, `name` TEXT NOT NULL, `selected` INTEGER NOT NULL, `category` TEXT NOT NULL, `hours` INTEGER NOT NULL, `completed` INTEGER NOT NULL, `available` INTEGER NOT NULL, `current` INTEGER NOT NULL, `selectable` INTEGER NOT NULL, `unavailable` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "selected", + "columnName": "selected", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hours", + "columnName": "hours", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "completed", + "columnName": "completed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "available", + "columnName": "available", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "selectable", + "columnName": "selectable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unavailable", + "columnName": "unavailable", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SagresFlags", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `demand_open` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "demandOpen", + "columnName": "demand_open", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Contributor", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `login` TEXT NOT NULL, `total` INTEGER NOT NULL, `name` TEXT NOT NULL, `image` TEXT, `link` TEXT, `url` TEXT, `bio` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "login", + "columnName": "login", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "total", + "columnName": "total", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "bio", + "columnName": "bio", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_Contributor_login", + "unique": true, + "columnNames": [ + "login" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Contributor_login` ON `${TABLE_NAME}` (`login`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "ServiceRequest", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service` TEXT NOT NULL, `date` TEXT NOT NULL, `amount` INTEGER NOT NULL, `situation` TEXT NOT NULL, `value` TEXT NOT NULL, `observation` TEXT NOT NULL, `notify` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "service", + "columnName": "service", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "situation", + "columnName": "situation", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "observation", + "columnName": "observation", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notify", + "columnName": "notify", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "service_uniqueness", + "unique": true, + "columnNames": [ + "service", + "date" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `service_uniqueness` ON `${TABLE_NAME}` (`service`, `date`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `imageUrl` TEXT, `username` TEXT NOT NULL, `email` TEXT, `darkThemeEnabled` INTEGER NOT NULL, `darkThemeInvites` INTEGER NOT NULL, `grouping` INTEGER, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "darkThemeEnabled", + "columnName": "darkThemeEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "darkThemeInvites", + "columnName": "darkThemeInvites", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "grouping", + "columnName": "grouping", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "STeacher", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`teacherId` INTEGER NOT NULL, `name` TEXT NOT NULL, `imageUrl` TEXT, PRIMARY KEY(`teacherId`))", + "fields": [ + { + "fieldPath": "teacherId", + "columnName": "teacherId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "teacherId" + ] + }, + "indices": [ + { + "name": "index_STeacher_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_STeacher_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "SDiscipline", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`disciplineId` INTEGER NOT NULL, `department` TEXT NOT NULL, `departmentName` TEXT, `code` TEXT NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`disciplineId`))", + "fields": [ + { + "fieldPath": "disciplineId", + "columnName": "disciplineId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "department", + "columnName": "department", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "departmentName", + "columnName": "departmentName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "disciplineId" + ] + }, + "indices": [ + { + "name": "index_SDiscipline_code_department", + "unique": true, + "columnNames": [ + "code", + "department" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SDiscipline_code_department` ON `${TABLE_NAME}` (`code`, `department`)" + }, + { + "name": "index_SDiscipline_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SDiscipline_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_SDiscipline_code", + "unique": false, + "columnNames": [ + "code" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SDiscipline_code` ON `${TABLE_NAME}` (`code`)" + }, + { + "name": "index_SDiscipline_department", + "unique": false, + "columnNames": [ + "department" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SDiscipline_department` ON `${TABLE_NAME}` (`department`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "SStudent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `userId` INTEGER NOT NULL, `name` TEXT NOT NULL, `imageUrl` TEXT, `course` INTEGER, `courseName` TEXT, `me` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "course", + "columnName": "course", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "courseName", + "columnName": "courseName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "me", + "columnName": "me", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SStudent_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SStudent_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "EvaluationEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `referencedId` INTEGER NOT NULL, `name` TEXT NOT NULL, `extra` TEXT, `image` TEXT, `type` INTEGER NOT NULL, `searchable` TEXT NOT NULL, `comp1` TEXT, `comp2` TEXT, `referenceLong1` INTEGER, `referenceLong2` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referencedId", + "columnName": "referencedId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "extra", + "columnName": "extra", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "comp1", + "columnName": "comp1", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "comp2", + "columnName": "comp2", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "referenceLong1", + "columnName": "referenceLong1", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "referenceLong2", + "columnName": "referenceLong2", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_EvaluationEntity_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EvaluationEntity_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Flowchart", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `courseId` INTEGER NOT NULL, `description` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "courseId", + "columnName": "courseId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastUpdated", + "columnName": "lastUpdated", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "FlowchartSemester", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `flowchartId` INTEGER NOT NULL, `order` INTEGER NOT NULL, `name` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`flowchartId`) REFERENCES `Flowchart`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "flowchartId", + "columnName": "flowchartId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FlowchartSemester_flowchartId", + "unique": false, + "columnNames": [ + "flowchartId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartSemester_flowchartId` ON `${TABLE_NAME}` (`flowchartId`)" + } + ], + "foreignKeys": [ + { + "table": "Flowchart", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "flowchartId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "FlowchartDiscipline", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `disciplineId` INTEGER NOT NULL, `type` TEXT NOT NULL, `mandatory` INTEGER NOT NULL, `semesterId` INTEGER NOT NULL, `completed` INTEGER NOT NULL, `participating` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`disciplineId`) REFERENCES `Discipline`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`semesterId`) REFERENCES `FlowchartSemester`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "disciplineId", + "columnName": "disciplineId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mandatory", + "columnName": "mandatory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semesterId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "completed", + "columnName": "completed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "participating", + "columnName": "participating", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FlowchartDiscipline_disciplineId", + "unique": false, + "columnNames": [ + "disciplineId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartDiscipline_disciplineId` ON `${TABLE_NAME}` (`disciplineId`)" + }, + { + "name": "index_FlowchartDiscipline_semesterId", + "unique": false, + "columnNames": [ + "semesterId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartDiscipline_semesterId` ON `${TABLE_NAME}` (`semesterId`)" + } + ], + "foreignKeys": [ + { + "table": "Discipline", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "disciplineId" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "FlowchartSemester", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "semesterId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "FlowchartRequirement", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `type` TEXT NOT NULL, `disciplineId` INTEGER NOT NULL, `requiredDisciplineId` INTEGER, `coursePercentage` REAL, `courseHours` INTEGER, `typeId` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`disciplineId`) REFERENCES `FlowchartDiscipline`(`id`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`requiredDisciplineId`) REFERENCES `FlowchartDiscipline`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "disciplineId", + "columnName": "disciplineId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "requiredDisciplineId", + "columnName": "requiredDisciplineId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "coursePercentage", + "columnName": "coursePercentage", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "courseHours", + "columnName": "courseHours", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "typeId", + "columnName": "typeId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FlowchartRequirement_disciplineId", + "unique": false, + "columnNames": [ + "disciplineId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartRequirement_disciplineId` ON `${TABLE_NAME}` (`disciplineId`)" + }, + { + "name": "index_FlowchartRequirement_requiredDisciplineId", + "unique": false, + "columnNames": [ + "requiredDisciplineId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FlowchartRequirement_requiredDisciplineId` ON `${TABLE_NAME}` (`requiredDisciplineId`)" + } + ], + "foreignKeys": [ + { + "table": "FlowchartDiscipline", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "disciplineId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "FlowchartDiscipline", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "requiredDisciplineId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "ProfileStatement", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `receiverId` INTEGER NOT NULL, `senderId` INTEGER NOT NULL, `senderName` TEXT, `senderPicture` TEXT, `hidden` INTEGER NOT NULL, `text` TEXT NOT NULL, `likes` INTEGER NOT NULL, `approved` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "receiverId", + "columnName": "receiverId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "senderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "senderName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "senderPicture", + "columnName": "senderPicture", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "likes", + "columnName": "likes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "approved", + "columnName": "approved", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "UserSession", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` TEXT NOT NULL, `started` INTEGER NOT NULL, `lastInteraction` INTEGER, `synced` INTEGER NOT NULL, `clickedAd` INTEGER NOT NULL, `impressionAd` INTEGER NOT NULL, PRIMARY KEY(`uid`))", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "started", + "columnName": "started", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastInteraction", + "columnName": "lastInteraction", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "synced", + "columnName": "synced", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "clickedAd", + "columnName": "clickedAd", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "impressionAd", + "columnName": "impressionAd", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uid" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AffinityQuestion", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `question` TEXT NOT NULL, `answered` INTEGER NOT NULL, `synced` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "question", + "columnName": "question", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "answered", + "columnName": "answered", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "synced", + "columnName": "synced", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AffinityQuestionAlternative", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `question_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, FOREIGN KEY(`question_id`) REFERENCES `AffinityQuestion`(`id`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`student_id`) REFERENCES `SStudent`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "questionId", + "columnName": "question_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_AffinityQuestionAlternative_student_id", + "unique": false, + "columnNames": [ + "student_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AffinityQuestionAlternative_student_id` ON `${TABLE_NAME}` (`student_id`)" + }, + { + "name": "index_AffinityQuestionAlternative_question_id", + "unique": false, + "columnNames": [ + "question_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AffinityQuestionAlternative_question_id` ON `${TABLE_NAME}` (`question_id`)" + } + ], + "foreignKeys": [ + { + "table": "AffinityQuestion", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "question_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "SStudent", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "student_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "Event", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `description` TEXT NOT NULL, `imageUrl` TEXT NOT NULL, `creatorName` TEXT NOT NULL, `creatorId` INTEGER NOT NULL, `offeredBy` TEXT NOT NULL, `startDate` TEXT NOT NULL, `endDate` TEXT NOT NULL, `location` TEXT NOT NULL, `price` REAL, `certificateHours` INTEGER, `courseId` INTEGER, `featured` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `approved` INTEGER NOT NULL, `canModify` INTEGER NOT NULL, `participating` INTEGER NOT NULL, `fakeTemp` INTEGER, `sending` INTEGER, `registerPage` TEXT, `canApprove` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "creatorName", + "columnName": "creatorName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "creatorId", + "columnName": "creatorId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "offeredBy", + "columnName": "offeredBy", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "startDate", + "columnName": "startDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "endDate", + "columnName": "endDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "location", + "columnName": "location", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "certificateHours", + "columnName": "certificateHours", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "courseId", + "columnName": "courseId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "featured", + "columnName": "featured", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "approved", + "columnName": "approved", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canModify", + "columnName": "canModify", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "participating", + "columnName": "participating", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fakeTemp", + "columnName": "fakeTemp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sending", + "columnName": "sending", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "registerPage", + "columnName": "registerPage", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "canApprove", + "columnName": "canApprove", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ClassGroupTeacher", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `classGroupId` INTEGER NOT NULL, `teacherId` INTEGER NOT NULL, FOREIGN KEY(`classGroupId`) REFERENCES `ClassGroup`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`teacherId`) REFERENCES `Teacher`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classGroupId", + "columnName": "classGroupId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacherId", + "columnName": "teacherId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + }, + "indices": [ + { + "name": "index_ClassGroupTeacher_classGroupId_teacherId", + "unique": true, + "columnNames": [ + "classGroupId", + "teacherId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassGroupTeacher_classGroupId_teacherId` ON `${TABLE_NAME}` (`classGroupId`, `teacherId`)" + }, + { + "name": "index_ClassGroupTeacher_teacherId", + "unique": false, + "columnNames": [ + "teacherId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassGroupTeacher_teacherId` ON `${TABLE_NAME}` (`teacherId`)" + }, + { + "name": "index_ClassGroupTeacher_classGroupId", + "unique": false, + "columnNames": [ + "classGroupId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_ClassGroupTeacher_classGroupId` ON `${TABLE_NAME}` (`classGroupId`)" + } + ], + "foreignKeys": [ + { + "table": "ClassGroup", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "classGroupId" + ], + "referencedColumns": [ + "uid" + ] + }, + { + "table": "Teacher", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "teacherId" + ], + "referencedColumns": [ + "uid" + ] + } + ] + }, + { + "tableName": "EdgeAccessToken", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accessToken` TEXT NOT NULL, `id` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "accessToken", + "columnName": "accessToken", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "EdgeServiceAccount", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `email` TEXT, `imageUrl` TEXT, `me` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "me", + "columnName": "me", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "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, '243bd94a71ae533546d4d1081f5ad83e')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 932bddf7f..a2137c7d9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,7 +33,6 @@ - + fun provideInterceptor( + database: UDatabase, + @Named("webViewUA") userAgent: String + ) = Interceptor { chain -> val request = chain.request() val host = request.url.host - if (host.contains(Constants.UNES_SERVICE_BASE_URL, ignoreCase = true)) { + // Normal contains edge + if (host.contains(Constants.EDGE_UNES_SERVICE_BASE_URL, ignoreCase = true)) { val builder = request.headers.newBuilder() .add("Accept", "application/json") + .set("User-Agent", userAgent) + + val token = runBlocking { database.edgeAccessToken.require() } + if (token?.accessToken != null) { + builder.add("Authorization", "Bearer ${token.accessToken}") + } + + val newHeaders = builder.build() + val renewed = request.newBuilder().headers(newHeaders).build() + + chain.proceed(renewed) + } else if (host.contains(Constants.UNES_SERVICE_BASE_URL, ignoreCase = true)) { + val builder = request.headers.newBuilder() + .add("Accept", "application/json") + .set("User-Agent", userAgent) val token = database.accessTokenDao().getAccessTokenDirect() if (token?.token != null) { @@ -120,7 +140,7 @@ object NetworkModule { val renewed = request.newBuilder().headers(newHeaders).build() chain.proceed(renewed) - } else { + } else { val nRequest = request.newBuilder().addHeader("Accept", "application/json").build() chain.proceed(nRequest) } @@ -176,22 +196,11 @@ object NetworkModule { .create(GithubService::class.java) } - @Provides - @Singleton - fun provideTemporaryService(client: OkHttpClient): APIService { - return Retrofit.Builder() - .baseUrl(Constants.UNES_SERVICE_UPDATE) - .client(client) - .addConverterFactory(GsonConverterFactory.create()) - .build() - .create(APIService::class.java) - } - @Provides @Singleton fun provideEdgeService(client: OkHttpClient): EdgeService { return Retrofit.Builder() - .baseUrl("https://edge-unes.forcetower.dev/api/") + .baseUrl(Constants.EDGE_UNES_SERVICE_URL) .client(client) .addConverterFactory(GsonConverterFactory.create()) .build() diff --git a/app/src/main/java/com/forcetower/uefs/core/model/edge/ChangePictureDTO.kt b/app/src/main/java/com/forcetower/uefs/core/model/edge/ChangePictureDTO.kt new file mode 100644 index 000000000..729ed51b2 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/edge/ChangePictureDTO.kt @@ -0,0 +1,8 @@ +package com.forcetower.uefs.core.model.edge + +import com.google.gson.annotations.SerializedName + +data class ChangePictureDTO( + @SerializedName("base64") + val base64: String +) diff --git a/app/src/main/java/com/forcetower/uefs/core/model/edge/EdgeAccessTokenDTO.kt b/app/src/main/java/com/forcetower/uefs/core/model/edge/EdgeAccessTokenDTO.kt new file mode 100644 index 000000000..5dbe192ad --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/edge/EdgeAccessTokenDTO.kt @@ -0,0 +1,8 @@ +package com.forcetower.uefs.core.model.edge + +import com.google.gson.annotations.SerializedName + +data class EdgeAccessTokenDTO( + @SerializedName("accessToken") + val accessToken: String +) \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/core/model/edge/EdgeLoginBody.kt b/app/src/main/java/com/forcetower/uefs/core/model/edge/EdgeLoginBody.kt new file mode 100644 index 000000000..c71d0d5bb --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/edge/EdgeLoginBody.kt @@ -0,0 +1,7 @@ +package com.forcetower.uefs.core.model.edge + +data class EdgeLoginBody( + val username: String, + val password: String, + val provider: String = "SNOWPIERCER" +) \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/core/model/edge/EmailLinkBodyDTO.kt b/app/src/main/java/com/forcetower/uefs/core/model/edge/EmailLinkBodyDTO.kt new file mode 100644 index 000000000..0a4dfb0fc --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/edge/EmailLinkBodyDTO.kt @@ -0,0 +1,8 @@ +package com.forcetower.uefs.core.model.edge + +import com.google.gson.annotations.SerializedName + +data class EmailLinkBodyDTO( + @SerializedName("email") + val email: String +) diff --git a/app/src/main/java/com/forcetower/uefs/core/model/edge/EmailLinkConfirmDTO.kt b/app/src/main/java/com/forcetower/uefs/core/model/edge/EmailLinkConfirmDTO.kt new file mode 100644 index 000000000..9745720c9 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/edge/EmailLinkConfirmDTO.kt @@ -0,0 +1,10 @@ +package com.forcetower.uefs.core.model.edge + +import com.google.gson.annotations.SerializedName + +data class EmailLinkConfirmDTO( + @SerializedName("code") + val code: String, + @SerializedName("securityToken") + val securityToken: String +) diff --git a/app/src/main/java/com/forcetower/uefs/core/model/edge/LinkEmailResponseDTO.kt b/app/src/main/java/com/forcetower/uefs/core/model/edge/LinkEmailResponseDTO.kt new file mode 100644 index 000000000..8402f8bc7 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/edge/LinkEmailResponseDTO.kt @@ -0,0 +1,8 @@ +package com.forcetower.uefs.core.model.edge + +import com.google.gson.annotations.SerializedName + +data class LinkEmailResponseDTO( + @SerializedName("securityToken") + val securityToken: String +) diff --git a/app/src/main/java/com/forcetower/uefs/core/model/edge/SendMessagingTokenDTO.kt b/app/src/main/java/com/forcetower/uefs/core/model/edge/SendMessagingTokenDTO.kt new file mode 100644 index 000000000..768b93d73 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/edge/SendMessagingTokenDTO.kt @@ -0,0 +1,8 @@ +package com.forcetower.uefs.core.model.edge + +import com.google.gson.annotations.SerializedName + +data class SendMessagingTokenDTO( + @SerializedName("token") + val token: String +) diff --git a/app/src/main/java/com/forcetower/uefs/core/model/edge/ServiceAccountDTO.kt b/app/src/main/java/com/forcetower/uefs/core/model/edge/ServiceAccountDTO.kt new file mode 100644 index 000000000..b436b2262 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/edge/ServiceAccountDTO.kt @@ -0,0 +1,14 @@ +package com.forcetower.uefs.core.model.edge + +import com.google.gson.annotations.SerializedName + +data class ServiceAccountDTO( + @SerializedName("id") + val id: String, + @SerializedName("name") + val name: String, + @SerializedName("email") + val email: String? = null, + @SerializedName("imageUrl") + val imageUrl: String? = null +) \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/core/model/edge/ServiceResponseWrapper.kt b/app/src/main/java/com/forcetower/uefs/core/model/edge/ServiceResponseWrapper.kt new file mode 100644 index 000000000..0ddf96367 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/edge/ServiceResponseWrapper.kt @@ -0,0 +1,14 @@ +package com.forcetower.uefs.core.model.edge + +import com.google.gson.annotations.SerializedName + +data class ServiceResponseWrapper( + @SerializedName("ok") + val ok: Boolean, + @SerializedName("data") + val data: T, + @SerializedName("message") + val message: String? = null, + @SerializedName("error") + val error: String? = null +) diff --git a/app/src/main/java/com/forcetower/uefs/core/model/ui/edge/EmailLinkComplete.kt b/app/src/main/java/com/forcetower/uefs/core/model/ui/edge/EmailLinkComplete.kt new file mode 100644 index 000000000..20f363b82 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/ui/edge/EmailLinkComplete.kt @@ -0,0 +1,9 @@ +package com.forcetower.uefs.core.model.ui.edge + +sealed interface EmailLinkComplete { + data object Linked : EmailLinkComplete + data object InvalidCode : EmailLinkComplete + data object EmailTaken : EmailLinkComplete + data object TooManyTries : EmailLinkComplete + data object ConnectionError : EmailLinkComplete +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/core/model/ui/edge/EmailLinkStart.kt b/app/src/main/java/com/forcetower/uefs/core/model/ui/edge/EmailLinkStart.kt new file mode 100644 index 000000000..37b325fa7 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/ui/edge/EmailLinkStart.kt @@ -0,0 +1,7 @@ +package com.forcetower.uefs.core.model.ui.edge + +sealed interface EmailLinkStart { + data class CodeSent(val securityCode: String): EmailLinkStart + data object InvalidInfo : EmailLinkStart + data object ConnectionError : EmailLinkStart +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/core/model/unes/ClassGroupTeacher.kt b/app/src/main/java/com/forcetower/uefs/core/model/unes/ClassGroupTeacher.kt new file mode 100644 index 000000000..eb2fc1bc5 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/unes/ClassGroupTeacher.kt @@ -0,0 +1,21 @@ +package com.forcetower.uefs.core.model.unes + +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Index +import androidx.room.PrimaryKey + +@Entity(indices = [ + Index(value = ["classGroupId", "teacherId"], unique = true), + Index(value = ["teacherId"], unique = false), + Index(value = ["classGroupId"], unique = false), +], foreignKeys = [ + ForeignKey(entity = ClassGroup::class, parentColumns = ["uid"], childColumns = ["classGroupId"], onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE), + ForeignKey(entity = Teacher::class, parentColumns = ["uid"], childColumns = ["teacherId"], onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE) +]) +data class ClassGroupTeacher( + @PrimaryKey(autoGenerate = true) + val uid: Int, + val classGroupId: Long, + val teacherId: Long +) \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/core/model/unes/EdgeAccessToken.kt b/app/src/main/java/com/forcetower/uefs/core/model/unes/EdgeAccessToken.kt new file mode 100644 index 000000000..4937f140b --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/unes/EdgeAccessToken.kt @@ -0,0 +1,11 @@ +package com.forcetower.uefs.core.model.unes + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity +data class EdgeAccessToken( + val accessToken: String, + @PrimaryKey(autoGenerate = false) + val id: Int = 1 +) \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/core/model/unes/EdgeServiceAccount.kt b/app/src/main/java/com/forcetower/uefs/core/model/unes/EdgeServiceAccount.kt new file mode 100644 index 000000000..75cec6190 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/model/unes/EdgeServiceAccount.kt @@ -0,0 +1,14 @@ +package com.forcetower.uefs.core.model.unes + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity +data class EdgeServiceAccount( + @PrimaryKey + val id: String, + val name: String, + val email: String?, + val imageUrl: String?, + val me: Boolean +) diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/database/Migrations.kt b/app/src/main/java/com/forcetower/uefs/core/storage/database/Migrations.kt index 0c15275e7..015b00e72 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/database/Migrations.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/database/Migrations.kt @@ -430,3 +430,13 @@ object M51TO52 : Migration(51, 52) { db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_Teacher_sagresId` ON `Teacher` (`sagresId`)") } } + +object M52TO53 : Migration(52, 53) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("CREATE TABLE IF NOT EXISTS `ClassGroupTeacher` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `classGroupId` INTEGER NOT NULL, `teacherId` INTEGER NOT NULL, FOREIGN KEY(`classGroupId`) REFERENCES `ClassGroup`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`teacherId`) REFERENCES `Teacher`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )") + db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_ClassGroupTeacher_classGroupId_teacherId` ON `ClassGroupTeacher` (`classGroupId`, `teacherId`)") + db.execSQL("CREATE INDEX IF NOT EXISTS `index_ClassGroupTeacher_teacherId` ON `ClassGroupTeacher` (`teacherId`)") + db.execSQL("CREATE INDEX IF NOT EXISTS `index_ClassGroupTeacher_classGroupId` ON `ClassGroupTeacher` (`classGroupId`)") + db.execSQL("INSERT OR IGNORE INTO ClassGroupTeacher(classGroupId,teacherId) SELECT uid AS classGroupId, teacher_id AS teacherId FROM ClassGroup WHERE teacher_id IS NOT NULL") + } +} diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/database/UDatabase.kt b/app/src/main/java/com/forcetower/uefs/core/storage/database/UDatabase.kt index 02dc244f4..3320f11dc 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/database/UDatabase.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/database/UDatabase.kt @@ -20,6 +20,7 @@ package com.forcetower.uefs.core.storage.database +import androidx.room.AutoMigration import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.TypeConverters @@ -32,12 +33,15 @@ import com.forcetower.uefs.core.model.unes.CalendarItem import com.forcetower.uefs.core.model.unes.Class import com.forcetower.uefs.core.model.unes.ClassAbsence import com.forcetower.uefs.core.model.unes.ClassGroup +import com.forcetower.uefs.core.model.unes.ClassGroupTeacher import com.forcetower.uefs.core.model.unes.ClassItem import com.forcetower.uefs.core.model.unes.ClassLocation import com.forcetower.uefs.core.model.unes.ClassMaterial import com.forcetower.uefs.core.model.unes.Contributor import com.forcetower.uefs.core.model.unes.Course import com.forcetower.uefs.core.model.unes.Discipline +import com.forcetower.uefs.core.model.unes.EdgeAccessToken +import com.forcetower.uefs.core.model.unes.EdgeServiceAccount import com.forcetower.uefs.core.model.unes.EvaluationEntity import com.forcetower.uefs.core.model.unes.Event import com.forcetower.uefs.core.model.unes.Flowchart @@ -67,6 +71,7 @@ import com.forcetower.uefs.core.storage.database.dao.CalendarDao import com.forcetower.uefs.core.storage.database.dao.ClassAbsenceDao import com.forcetower.uefs.core.storage.database.dao.ClassDao import com.forcetower.uefs.core.storage.database.dao.ClassGroupDao +import com.forcetower.uefs.core.storage.database.dao.ClassGroupTeacherDao import com.forcetower.uefs.core.storage.database.dao.ClassItemDao import com.forcetower.uefs.core.storage.database.dao.ClassLocationDao import com.forcetower.uefs.core.storage.database.dao.ClassMaterialDao @@ -76,6 +81,8 @@ import com.forcetower.uefs.core.storage.database.dao.DemandOfferDao import com.forcetower.uefs.core.storage.database.dao.DisciplineDao import com.forcetower.uefs.core.storage.database.dao.DisciplineServiceDao import com.forcetower.uefs.core.storage.database.dao.DocumentDao +import com.forcetower.uefs.core.storage.database.dao.EdgeAccessTokenDao +import com.forcetower.uefs.core.storage.database.dao.EdgeServiceAccountDao import com.forcetower.uefs.core.storage.database.dao.EvaluationEntitiesDao import com.forcetower.uefs.core.storage.database.dao.EventDao import com.forcetower.uefs.core.storage.database.dao.FlagsDao @@ -133,10 +140,17 @@ import com.forcetower.uefs.core.util.Converters UserSession::class, AffinityQuestion::class, AffinityQuestionAlternative::class, - Event::class + Event::class, + ClassGroupTeacher::class, + EdgeAccessToken::class, + EdgeServiceAccount::class ], - version = 52, - exportSchema = true + version = 55, + exportSchema = true, + autoMigrations = [ + AutoMigration(from = 53, to = 54), + AutoMigration(from = 54, to = 55), + ] ) @TypeConverters(value = [Converters::class]) abstract class UDatabase : RoomDatabase() { @@ -175,4 +189,8 @@ abstract class UDatabase : RoomDatabase() { abstract fun userSessionDao(): UserSessionDao abstract fun affinityQuestion(): AffinityQuestionDao abstract fun eventDao(): EventDao + abstract fun classGroupTeacher(): ClassGroupTeacherDao + + abstract val edgeAccessToken: EdgeAccessTokenDao + abstract val edgeServiceAccount: EdgeServiceAccountDao } diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/database/aggregation/ClassGroupWithData.kt b/app/src/main/java/com/forcetower/uefs/core/storage/database/aggregation/ClassGroupWithData.kt index dff3bf545..75ca6941c 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/database/aggregation/ClassGroupWithData.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/database/aggregation/ClassGroupWithData.kt @@ -21,13 +21,23 @@ package com.forcetower.uefs.core.storage.database.aggregation import androidx.room.Embedded +import androidx.room.Junction import androidx.room.Relation import com.forcetower.uefs.core.model.unes.Class import com.forcetower.uefs.core.model.unes.ClassGroup +import com.forcetower.uefs.core.model.unes.ClassGroupTeacher +import com.forcetower.uefs.core.model.unes.Teacher data class ClassGroupWithData( @Embedded val group: ClassGroup, @Relation(parentColumn = "class_id", entityColumn = "uid", entity = Class::class) - val classData: ClassWithDiscipline + val classData: ClassWithDiscipline, + @Relation( + entity = Teacher::class, + entityColumn = "uid", + parentColumn = "uid", + associateBy = Junction(value = ClassGroupTeacher::class, parentColumn = "classGroupId", entityColumn = "teacherId") + ) + val teachers: List ) diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/database/aggregation/ClassGroupWithTeachers.kt b/app/src/main/java/com/forcetower/uefs/core/storage/database/aggregation/ClassGroupWithTeachers.kt new file mode 100644 index 000000000..009347ef6 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/storage/database/aggregation/ClassGroupWithTeachers.kt @@ -0,0 +1,20 @@ +package com.forcetower.uefs.core.storage.database.aggregation + +import androidx.room.Embedded +import androidx.room.Junction +import androidx.room.Relation +import com.forcetower.uefs.core.model.unes.AffinityQuestionAlternative +import com.forcetower.uefs.core.model.unes.ClassGroup +import com.forcetower.uefs.core.model.unes.Teacher + +data class ClassGroupWithTeachers( + @Embedded + val data: ClassGroup, + @Relation( + entity = Teacher::class, + entityColumn = "id", + parentColumn = "id", + associateBy = Junction(value = AffinityQuestionAlternative::class, parentColumn = "classGroupId", entityColumn = "teacherId") + ) + val teachers: List +) \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/database/dao/ClassGroupTeacherDao.kt b/app/src/main/java/com/forcetower/uefs/core/storage/database/dao/ClassGroupTeacherDao.kt new file mode 100644 index 000000000..e851badf5 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/storage/database/dao/ClassGroupTeacherDao.kt @@ -0,0 +1,12 @@ +package com.forcetower.uefs.core.storage.database.dao + +import androidx.room.Dao +import androidx.room.Query +import com.forcetower.core.database.BaseDao +import com.forcetower.uefs.core.model.unes.ClassGroupTeacher + +@Dao +abstract class ClassGroupTeacherDao : BaseDao() { + @Query("DELETE FROM ClassGroupTeacher WHERE classGroupId = :classGroupId") + abstract suspend fun deleteAllFromClassGroup(classGroupId: Long) +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/database/dao/EdgeAccessTokenDao.kt b/app/src/main/java/com/forcetower/uefs/core/storage/database/dao/EdgeAccessTokenDao.kt new file mode 100644 index 000000000..d8750ed90 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/storage/database/dao/EdgeAccessTokenDao.kt @@ -0,0 +1,15 @@ +package com.forcetower.uefs.core.storage.database.dao + +import androidx.room.Dao +import androidx.room.Query +import com.forcetower.core.database.BaseDao +import com.forcetower.uefs.core.model.unes.EdgeAccessToken + +@Dao +abstract class EdgeAccessTokenDao : BaseDao() { + @Query("SELECT * FROM EdgeAccessToken LIMIT 1") + abstract suspend fun require(): EdgeAccessToken? + + @Query("DELETE FROM EdgeAccessToken") + abstract suspend fun deleteAll() +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/database/dao/EdgeServiceAccountDao.kt b/app/src/main/java/com/forcetower/uefs/core/storage/database/dao/EdgeServiceAccountDao.kt new file mode 100644 index 000000000..01f1b968a --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/storage/database/dao/EdgeServiceAccountDao.kt @@ -0,0 +1,19 @@ +package com.forcetower.uefs.core.storage.database.dao + +import androidx.room.Dao +import androidx.room.Query +import com.forcetower.core.database.BaseDao +import com.forcetower.uefs.core.model.unes.EdgeServiceAccount +import kotlinx.coroutines.flow.Flow + +@Dao +abstract class EdgeServiceAccountDao : BaseDao() { + @Query("SELECT * FROM EdgeServiceAccount WHERE me = 1 LIMIT 1") + abstract fun me(): Flow + + @Query("SELECT * FROM EdgeServiceAccount WHERE me = 1 LIMIT 1") + abstract suspend fun requireMe(): EdgeServiceAccount? + + @Query("DELETE FROM EdgeServiceAccount") + abstract suspend fun deleteAll() +} diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/database/dao/ProfileDao.kt b/app/src/main/java/com/forcetower/uefs/core/storage/database/dao/ProfileDao.kt index e535096b6..3b0fd4f31 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/database/dao/ProfileDao.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/database/dao/ProfileDao.kt @@ -30,6 +30,7 @@ import com.forcetower.core.utils.WordUtils import com.forcetower.sagres.database.model.SagresPerson import com.forcetower.uefs.core.model.unes.Profile import dev.forcetower.breaker.model.Person +import kotlinx.coroutines.flow.Flow import timber.log.Timber import java.util.Locale @@ -44,6 +45,9 @@ abstract class ProfileDao { @Query("SELECT * FROM Profile WHERE me = 1 LIMIT 1") abstract fun selectMe(): LiveData + @Query("SELECT * FROM Profile WHERE me = 1 LIMIT 1") + abstract fun me(): Flow + @Transaction open fun insert(person: SagresPerson, score: Double = -1.0) { val name = WordUtils.toTitleCase(person.name?.trim()) ?: "" diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/network/EdgeService.kt b/app/src/main/java/com/forcetower/uefs/core/storage/network/EdgeService.kt index 5e79a6b31..65e7f4411 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/network/EdgeService.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/network/EdgeService.kt @@ -1,24 +1,52 @@ package com.forcetower.uefs.core.storage.network import com.forcetower.uefs.core.model.edge.AssertionData +import com.forcetower.uefs.core.model.edge.ChangePictureDTO import com.forcetower.uefs.core.model.edge.CompleteAssertionData +import com.forcetower.uefs.core.model.edge.EdgeAccessTokenDTO +import com.forcetower.uefs.core.model.edge.EdgeLoginBody +import com.forcetower.uefs.core.model.edge.EmailLinkBodyDTO +import com.forcetower.uefs.core.model.edge.EmailLinkConfirmDTO +import com.forcetower.uefs.core.model.edge.LinkEmailResponseDTO import com.forcetower.uefs.core.model.edge.RegisterPasskeyCredential import com.forcetower.uefs.core.model.edge.RegisterPasskeyStart +import com.forcetower.uefs.core.model.edge.SendMessagingTokenDTO +import com.forcetower.uefs.core.model.edge.ServiceAccountDTO +import com.forcetower.uefs.core.model.edge.ServiceResponseWrapper import com.forcetower.uefs.core.model.unes.AccessToken +import retrofit2.Response import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST interface EdgeService { + @POST("auth/login/anonymous") + suspend fun loginAnonymous(@Body data: EdgeLoginBody): EdgeAccessTokenDTO + @GET("auth/login/passkey/assertion/start") suspend fun startAssertion(): AssertionData @POST("auth/login/passkey/assertion/finish") - suspend fun completeAssertion(@Body data: CompleteAssertionData): AccessToken + suspend fun completeAssertion(@Body data: CompleteAssertionData): EdgeAccessTokenDTO @GET("passkeys/register/start") suspend fun registerPasskeyStart(): RegisterPasskeyStart @POST("passkeys/register/finish") suspend fun registerPasskeyFinish(@Body data: RegisterPasskeyCredential) + + @GET("account/me") + suspend fun me(): ServiceResponseWrapper + + @POST("account/register/start") + suspend fun linkEmailStart(@Body data: EmailLinkBodyDTO): Response> + + @POST("account/register/complete") + suspend fun linkEmailComplete(@Body data: EmailLinkConfirmDTO): Response> + + @POST("account/fcm") + suspend fun fcm(@Body data: SendMessagingTokenDTO): ServiceResponseWrapper + + @POST("account/picture") + suspend fun uploadPicture(@Body data: ChangePictureDTO): ServiceResponseWrapper } diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/network/UService.kt b/app/src/main/java/com/forcetower/uefs/core/storage/network/UService.kt index 9b85816c9..7310af36f 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/network/UService.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/network/UService.kt @@ -139,6 +139,9 @@ interface UService { @POST("account/update_fcm") fun sendToken(@Body data: Map): Call> + @POST("account/update_fcm") + suspend fun sendTokenSuspend(@Body data: Map): UResponse + @GET("account") fun getAccount(): Call diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/repository/AdventureRepository.kt b/app/src/main/java/com/forcetower/uefs/core/storage/repository/AdventureRepository.kt index 910bd3b66..467c8c2d4 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/repository/AdventureRepository.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/repository/AdventureRepository.kt @@ -37,7 +37,6 @@ import com.forcetower.uefs.core.model.unes.Semester import com.forcetower.uefs.core.storage.database.UDatabase import com.forcetower.uefs.core.storage.network.UService import com.forcetower.uefs.feature.shared.extensions.generateCalendarFromHour -import com.google.firebase.auth.FirebaseAuth import java.util.Calendar import java.util.Locale import java.util.concurrent.TimeUnit @@ -47,7 +46,6 @@ import kotlin.collections.set class AdventureRepository @Inject constructor( private val database: UDatabase, private val executors: AppExecutors, - private val auth: FirebaseAuth, private val preferences: SharedPreferences, private val locations: AchLocationsRepository, private val service: UService @@ -102,9 +100,6 @@ class AdventureRepository @Inject constructor( @WorkerThread private fun internalCheckAchievements(): Map { val data = HashMap() - - auth.currentUser ?: return data - performCheckAchievements(data) return data } @@ -193,9 +188,9 @@ class AdventureRepository @Inject constructor( clazz.grades.forEach { grade -> valid = true - val number = grade.gradeDouble() ?: -1 - if (number == 7) data[R.string.achievement_medocre] = -1 - if (number == 10) data[R.string.achievement_achei_fcil] = -1 + val number = grade.gradeDouble() ?: -1.0 + if (number == 7.0) data[R.string.achievement_medocre] = -1 + if (number == 10.0) data[R.string.achievement_achei_fcil] = -1 } if (clazz.grades.size == 3) { @@ -266,9 +261,9 @@ class AdventureRepository @Inject constructor( if (points in 9.5..9.9) data[R.string.achievement_to_perto_mas_to_longe] = -1 clazz.grades.forEach { grade -> - val number = grade.gradeDouble() ?: -1 - if (number == 7) data[R.string.achievement_medocre] = -1 - if (number == 10) data[R.string.achievement_achei_fcil] = -1 + val number = grade.gradeDouble() ?: -1.0 + if (number == 7.0) data[R.string.achievement_medocre] = -1 + if (number == 10.0) data[R.string.achievement_achei_fcil] = -1 } if (clazz.grades.size == 3) { diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/repository/FeedbackRepository.kt b/app/src/main/java/com/forcetower/uefs/core/storage/repository/FeedbackRepository.kt index 41aab97d9..e59663074 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/repository/FeedbackRepository.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/repository/FeedbackRepository.kt @@ -20,68 +20,11 @@ package com.forcetower.uefs.core.storage.repository -import android.os.Build -import androidx.annotation.AnyThread -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import com.forcetower.uefs.AppExecutors -import com.forcetower.uefs.core.model.service.Feedback -import com.forcetower.uefs.core.model.unes.Access import com.forcetower.uefs.core.storage.database.UDatabase -import com.forcetower.uefs.feature.shared.extensions.toBase64 -import com.google.android.gms.tasks.Tasks -import com.google.firebase.auth.FirebaseAuth -import com.google.firebase.firestore.CollectionReference -import com.google.firebase.messaging.FirebaseMessaging -import timber.log.Timber import javax.inject.Inject -import javax.inject.Named import javax.inject.Singleton @Singleton class FeedbackRepository @Inject constructor( - private val firebaseAuth: FirebaseAuth, - private val executors: AppExecutors, - private val database: UDatabase, - @Named(Feedback.COLLECTION) - private val collection: CollectionReference -) { - - @AnyThread - fun onSendFeedback(text: String): LiveData { - val result = MutableLiveData() - executors.diskIO().execute { - val userId = firebaseAuth.currentUser?.uid - val access = database.accessDao().getAccessDirect() - ?: Access(username = "random", password = "user") - val profile = database.profileDao().selectMeDirect() - val feedback = Feedback( - text = text, - username = access.username, - course = profile?.course, - email = profile?.email, - hash = access.toString().toBase64(), - firebaseId = userId, - manufacturer = Build.MANUFACTURER, - deviceModel = Build.MODEL, - android = Build.VERSION.SDK_INT - ) - var token: String? = null - try { - token = Tasks.await(FirebaseMessaging.getInstance().token) - } catch (throwable: Throwable) { - Timber.e(throwable) - } - - feedback.currentToken = token - - try { - Tasks.await(collection.add(feedback)) - } catch (throwable: Throwable) { - Timber.e(throwable) - } - } - result.postValue(true) - return result - } -} + private val database: UDatabase +) diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/repository/FirebaseAuthRepository.kt b/app/src/main/java/com/forcetower/uefs/core/storage/repository/FirebaseAuthRepository.kt deleted file mode 100644 index e86db7603..000000000 --- a/app/src/main/java/com/forcetower/uefs/core/storage/repository/FirebaseAuthRepository.kt +++ /dev/null @@ -1,202 +0,0 @@ -/* - * This file is part of the UNES Open Source Project. - * UNES is licensed under the GNU GPLv3. - * - * Copyright (c) 2020. João Paulo Sena - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.forcetower.uefs.core.storage.repository - -import android.content.Context -import android.content.SharedPreferences -import com.forcetower.core.utils.WordUtils -import com.forcetower.sagres.database.model.SagresPerson -import com.forcetower.uefs.AppExecutors -import com.forcetower.uefs.R -import com.forcetower.uefs.core.model.unes.Access -import com.forcetower.uefs.core.model.unes.Course -import com.forcetower.uefs.core.model.unes.Profile -import com.google.android.gms.tasks.Tasks -import com.google.firebase.auth.FirebaseAuth -import com.google.firebase.auth.FirebaseUser -import com.google.firebase.firestore.CollectionReference -import com.google.firebase.firestore.SetOptions -import com.google.firebase.messaging.FirebaseMessaging -import timber.log.Timber -import java.util.Locale -import javax.inject.Inject -import javax.inject.Named -import javax.inject.Singleton - -@Singleton -class FirebaseAuthRepository @Inject constructor( - private val firebaseAuth: FirebaseAuth, - @Named(Profile.COLLECTION) private val userCollection: CollectionReference, - private val context: Context, - private val executors: AppExecutors, - private val preferences: SharedPreferences -) { - private val secret = context.getString(R.string.firebase_account_secret) - - fun loginToFirebase(person: SagresPerson, access: Access, reconnect: Boolean = false) { - if (reconnect) { firebaseAuth.signOut() } - if (firebaseAuth.currentUser == null) { - val user = access.username.lowercase(Locale.getDefault()) - val username = if (user.contains("@")) { - "${user.substring(0, user.indexOf("@"))}_email" - } else { - user - } - - val email = context.getString(R.string.email_unes_format, username) - - val profiler = "__${username}__" - val password = context.getString(R.string.firebase_password_pattern, username, profiler, secret) - - attemptSignIn( - email, - context.getString(R.string.firebase_password_pattern, username, password, secret), - access, - person - ) - } - } - - private fun attemptSignIn(email: String, password: String, access: Access, person: SagresPerson) { - Timber.d("Attempt Login") - firebaseAuth.signInWithEmailAndPassword(email, password) - .addOnCompleteListener( - executors.others(), - { task -> - if (task.isSuccessful) { - val user = firebaseAuth.currentUser - if (user == null) { - attemptCreateAccount(email, password, access, person) - } else { - Timber.d("Connected! Your account is: $user") - connected(access, person, user.uid) - } - } else { - Timber.d("Failed to Sign In...") - Timber.d("Exception: ${task.exception}") - attemptCreateAccount(email, password, access, person) - } - } - ) - } - - private fun attemptCreateAccount(email: String, password: String, access: Access, person: SagresPerson) { - Timber.d("Attempt Create account") - firebaseAuth.createUserWithEmailAndPassword(email, password) - .addOnCompleteListener( - executors.others(), - { task -> - if (task.isSuccessful) { - val user = firebaseAuth.currentUser - if (user == null) { - Timber.d("Failed anyways") - } else { - Timber.d("Connected! Your account is: $user") - connected(access, person, user.uid) - } - } else { - Timber.d("Failed to Create account...") - Timber.d("Exception: ${task.exception}") - } - } - ) - } - - private fun connected(access: Access, person: SagresPerson, uid: String) { - Timber.d("Creating student profile for ${person.name?.trim()} UID: $uid") - - val data = mutableMapOf( - "name" to WordUtils.toTitleCase(person.name?.trim()), - "username" to access.username, - "email" to (person.email?.trim()?.lowercase(Locale.getDefault()) ?: "unknown@unes.com"), - "cpf" to person.getCpf()?.trim(), - "sagresId" to person.id, - "imageUrl" to "/users/$uid/avatar.jpg", - "manufacturer" to android.os.Build.MANUFACTURER, - "model" to android.os.Build.MODEL - ) - - val idTask = FirebaseMessaging.getInstance().token - try { - val result = Tasks.await(idTask) - preferences.edit().putString("current_firebase_token", result).apply() - data["firebaseToken"] = result - } catch (t: Throwable) { - Timber.e(t) - } - - userCollection.document(uid).set(data, SetOptions.merge()) - .addOnCompleteListener( - executors.others(), - { task -> - if (task.isSuccessful) { - Timber.d("User data set!") - } else { - Timber.d("Failed to set data...") - Timber.d("Exception: ${task.exception}") - } - } - ) - } - - fun updateCourse(course: Course, user: FirebaseUser) { - val data = mapOf( - "courseId" to course.id, - "course" to course.name - ) - userCollection.document(user.uid).set(data, SetOptions.merge()) - .addOnCompleteListener( - executors.others(), - { task -> - if (task.isSuccessful) { - Timber.d("User course data set!") - } else { - Timber.d("Failed to set course data...") - Timber.d("Exception: ${task.exception}") - } - } - ) - } - - fun updateFrequency(value: Int) { - val user = firebaseAuth.currentUser - user ?: return - - val data = mapOf("syncFrequency" to value) - userCollection.document(user.uid).set(data, SetOptions.merge()) - .addOnCompleteListener( - executors.others(), - { task -> - if (task.isSuccessful) { - Timber.d("Completed setting frequency") - } else { - Timber.d("Failed to set frequency") - Timber.d("Exception: ${task.exception}") - } - } - ) - } - - fun reconnect(): Boolean { - firebaseAuth.signOut() - return true - } -} diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/repository/FirebaseMessageRepository.kt b/app/src/main/java/com/forcetower/uefs/core/storage/repository/FirebaseMessageRepository.kt index 4eba44c37..c218bcd3c 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/repository/FirebaseMessageRepository.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/repository/FirebaseMessageRepository.kt @@ -29,9 +29,11 @@ import androidx.work.WorkManager import com.forcetower.sagres.SagresNavigator import com.forcetower.uefs.AppExecutors import com.forcetower.uefs.BuildConfig +import com.forcetower.uefs.core.model.edge.SendMessagingTokenDTO import com.forcetower.uefs.core.model.unes.Message import com.forcetower.uefs.core.notification.StatementNotificationProcessor import com.forcetower.uefs.core.storage.database.UDatabase +import com.forcetower.uefs.core.storage.network.EdgeService import com.forcetower.uefs.core.storage.network.UService import com.forcetower.uefs.core.work.hourglass.HourglassContributeWorker import com.forcetower.uefs.core.work.sync.SyncLinkedWorker @@ -41,6 +43,7 @@ import com.forcetower.uefs.service.NotificationCreator import com.google.android.gms.tasks.Tasks import com.google.firebase.messaging.FirebaseMessaging import com.google.firebase.messaging.RemoteMessage +import kotlinx.coroutines.tasks.await import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -48,11 +51,11 @@ import javax.inject.Singleton @Singleton class FirebaseMessageRepository @Inject constructor( private val service: UService, + private val edgeService: EdgeService, private val database: UDatabase, private val preferences: SharedPreferences, private val context: Context, private val syncRepository: SagresSyncRepository, - private val firebaseAuthRepository: FirebaseAuthRepository, private val firebaseMessaging: FirebaseMessaging, private val executors: AppExecutors ) { @@ -73,7 +76,6 @@ class FirebaseMessageRepository @Inject constructor( "remote_database" -> promoteDatabase(data) "service" -> serviceNotificationExtractor(data) "synchronize" -> universalSync() - "reconnect_firebase" -> firebaseReconnect(data) "reschedule_sync" -> rescheduleSync(data) "hourglass_initiator" -> hourglassRunner() "worker_cancel" -> cancelWorker(data) @@ -171,29 +173,6 @@ class FirebaseMessageRepository @Inject constructor( } } - private fun firebaseReconnect(data: Map) { - // Esta função se tornou necessária ja que eu fiz a besteira de não colocar um toLowerCase nos nomes - // que compõe as credenciais do firebase, isso poderia fazer com que todos os usuarios precisassem se reconectar. - // Então, basta invocar esta função que a pessoa irá se reconectar aos serviços do firebase sem problemas. - - val unique = data["unique"] - val version = data["version"]?.toIntOrNull() - if (unique == null || version == null) { - Timber.e("You need to specify a unique key and a version for this to work") - return - } - - val executed = preferences.getBoolean("${unique}__firebase", false) - if (BuildConfig.VERSION_CODE >= version || executed) { - Timber.d("Invalid version code($version) or executed($executed)") - return - } - - val completed = firebaseAuthRepository.reconnect() - Timber.d("Finished execution >> Completed: $completed") - preferences.edit().putBoolean("${unique}__firebase", completed).apply() - } - private suspend fun universalSync() { syncRepository.performSync("Universal") } @@ -302,15 +281,19 @@ class FirebaseMessageRepository @Inject constructor( NotificationCreator.showSimpleNotification(context, title, content) } - fun onNewToken(token: String) { - val auth = database.accessTokenDao().getAccessTokenDirect() + suspend fun onNewToken(token: String) { + val auth = database.accessTokenDao().getAccessTokenDirectSuspend() if (auth != null) { - try { - service.sendToken(mapOf("token" to token)).execute() - } catch (t: Throwable) { } + runCatching { service.sendTokenSuspend(mapOf("token" to token)) } } else { - Timber.d("Disconnected") + Timber.d("Disconnected Base UNES") + } + + val auth2 = database.edgeAccessToken.require() + if (auth2 != null) { + runCatching { edgeService.fcm(SendMessagingTokenDTO(token)) } } + preferences.edit().putString("current_firebase_token", token).apply() } @@ -326,23 +309,13 @@ class FirebaseMessageRepository @Inject constructor( } } - @MainThread - fun sendNewTokenOrNot(): LiveData { - val result = MutableLiveData() - executors.diskIO().execute { - try { - sendNewToken() - result.postValue(true) - } catch (t: Throwable) { - result.postValue(false) - } + suspend fun sendNewTokenOrNot() { + try { + val task = FirebaseMessaging.getInstance().token + val value = task.await() + onNewToken(value) + } catch (e: Throwable) { + Timber.e(e, "Failed to update fcm token") } - return result - } - - private fun sendNewToken() { - val task = FirebaseMessaging.getInstance().token - val value = Tasks.await(task) - onNewToken(value) } } diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/repository/LoginSagresRepository.kt b/app/src/main/java/com/forcetower/uefs/core/storage/repository/LoginSagresRepository.kt index d97f98aa2..e33546d34 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/repository/LoginSagresRepository.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/repository/LoginSagresRepository.kt @@ -68,7 +68,6 @@ class LoginSagresRepository @Inject constructor( private val executor: AppExecutors, private val database: UDatabase, private val preferences: SharedPreferences, - private val firebaseAuthRepository: FirebaseAuthRepository, private val authRepository: AuthRepository, private val sessionRepository: CookieSessionRepository, private val context: Context @@ -163,7 +162,6 @@ class LoginSagresRepository @Inject constructor( val person = m.person if (person != null) { executor.diskIO().execute { database.profileDao().insert(person, score) } - executor.others().execute { firebaseAuthRepository.loginToFirebase(person, access, true) } messages(data, person.id) } else { Timber.d("SPerson is null") @@ -188,7 +186,6 @@ class LoginSagresRepository @Inject constructor( val name = SagresBasicParser.getName(document) ?: username val person = SagresPerson(username.hashCode().toLong(), name, name, "00000000000", username) executor.diskIO().execute { database.profileDao().insert(person, score) } - executor.others().execute { firebaseAuthRepository.loginToFirebase(person, access, true) } messages(data, null) } diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/repository/ProfileRepository.kt b/app/src/main/java/com/forcetower/uefs/core/storage/repository/ProfileRepository.kt index eee933c89..530da6ace 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/repository/ProfileRepository.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/repository/ProfileRepository.kt @@ -51,6 +51,8 @@ class ProfileRepository @Inject constructor( ) { fun getCommonProfile() = database.profileDao().selectMe() + fun me() = database.profileDao().me() + fun getMeProfileAsync() { executors.networkIO().execute { getMeProfileSync() diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/repository/RemindersRepository.kt b/app/src/main/java/com/forcetower/uefs/core/storage/repository/RemindersRepository.kt index 59272a9d1..6d6fd53cf 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/repository/RemindersRepository.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/repository/RemindersRepository.kt @@ -22,99 +22,26 @@ package com.forcetower.uefs.core.storage.repository import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import com.forcetower.uefs.AppExecutors import com.forcetower.uefs.core.model.service.Reminder -import com.forcetower.uefs.core.model.unes.Profile -import com.google.android.gms.tasks.OnCompleteListener -import com.google.firebase.auth.FirebaseAuth -import com.google.firebase.firestore.CollectionReference -import com.google.firebase.firestore.Query -import com.google.firebase.firestore.SetOptions -import timber.log.Timber import javax.inject.Inject -import javax.inject.Named import javax.inject.Singleton @Singleton -class RemindersRepository @Inject constructor( - private val executors: AppExecutors, - private val firebaseAuth: FirebaseAuth, - @Named(Profile.COLLECTION) private val profileReference: CollectionReference -) { - +class RemindersRepository @Inject constructor() { fun getReminders(): LiveData> { val data = MutableLiveData>() - val user = firebaseAuth.currentUser - if (user != null) { - profileReference.document(user.uid).collection(Reminder.COLLECTION).orderBy("createdAt", Query.Direction.DESCENDING).addSnapshotListener { snapshot, _ -> - if (snapshot != null) { - val list = snapshot.documents.map { it.toObject(Reminder::class.java)!!.apply { id = it.id } } - val deadline = list.filter { it.date != null }.sortedBy { it.date!! } - val common = list.filter { it.date == null } - val result = deadline + common - data.postValue(result) - } - } - } else { - Timber.d("Not connected") - } return data } fun createReminder(reminder: Reminder) { - val user = firebaseAuth.currentUser - if (user != null) { - profileReference.document(user.uid).collection(Reminder.COLLECTION).add(reminder).addOnCompleteListener { task -> - if (task.isSuccessful) { - Timber.d("Created") - } else { - Timber.d("Creation failed") - Timber.d("Exception message: ${task.exception?.message}") - } - } - } else { - Timber.d("Not connected") - } + } fun updateReminderCompleteStatus(reminder: Reminder, next: Boolean) { - val user = firebaseAuth.currentUser - if (user != null) { - Timber.d("Updating reminder: ${reminder.id} :: ${reminder.title}") - val data = mapOf("completed" to next) - profileReference.document(user.uid).collection(Reminder.COLLECTION).document(reminder.id) - .set(data, SetOptions.merge()) - .addOnCompleteListener( - executors.networkIO(), - OnCompleteListener { task -> - if (task.isSuccessful) { - Timber.d("Updated reminder") - } else { - Timber.d("Reminder was not updated") - Timber.d("Exception message: ${task.exception?.message}") - } - } - ) - } else { - Timber.d("Not connected") - } + } fun deleteReminder(reminder: Reminder) { - val user = firebaseAuth.currentUser - if (user != null) { - profileReference.document(user.uid).collection(Reminder.COLLECTION).document(reminder.id) - .delete() - .addOnCompleteListener { task -> - if (task.isSuccessful) { - Timber.d("Deleted reminder") - } else { - Timber.d("Reminder was not deleted") - Timber.d("Exception message: ${task.exception?.message}") - } - } - } else { - Timber.d("Not connected") - } + } } diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/repository/SagresDataRepository.kt b/app/src/main/java/com/forcetower/uefs/core/storage/repository/SagresDataRepository.kt index b71ef49d1..53245538f 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/repository/SagresDataRepository.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/repository/SagresDataRepository.kt @@ -32,7 +32,6 @@ import com.forcetower.uefs.core.storage.database.UDatabase import com.forcetower.uefs.core.storage.network.UService import com.forcetower.uefs.core.storage.resource.Resource import com.forcetower.uefs.core.util.round -import com.google.firebase.auth.FirebaseAuth import dev.forcetower.breaker.Orchestra import dev.forcetower.breaker.model.Authorization import kotlinx.coroutines.Dispatchers @@ -47,7 +46,6 @@ import javax.inject.Singleton class SagresDataRepository @Inject constructor( private val database: UDatabase, private val executor: AppExecutors, - private val firebaseAuth: FirebaseAuth, private val preferences: SharedPreferences, private val client: OkHttpClient, @Named("webViewUA") private val agent: String, @@ -58,7 +56,6 @@ class SagresDataRepository @Inject constructor( fun logout() { executor.diskIO().execute { - firebaseAuth.signOut() preferences.edit() .remove("hourglass_status") .apply() @@ -76,6 +73,11 @@ class SagresDataRepository @Inject constructor( } } + suspend fun logoutSuspend() { + database.edgeAccessToken.deleteAll() + database.edgeServiceAccount.deleteAll() + } + fun getFlags() = database.flagsDao().getFlags() fun getSemesters() = database.semesterDao().getParticipatingSemesters() fun getCourse() = database.profileDao().getProfileCourse() diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/repository/SagresSyncRepository.kt b/app/src/main/java/com/forcetower/uefs/core/storage/repository/SagresSyncRepository.kt index 848af1e70..77ad81753 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/repository/SagresSyncRepository.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/repository/SagresSyncRepository.kt @@ -93,7 +93,6 @@ class SagresSyncRepository @Inject constructor( private val executors: AppExecutors, private val authRepository: AuthRepository, private val adventureRepository: AdventureRepository, - private val firebaseAuthRepository: FirebaseAuthRepository, private val cookieSessionRepository: CookieSessionRepository, private val service: UService, private val remoteConfig: FirebaseRemoteConfig, @@ -284,16 +283,6 @@ class SagresSyncRepository @Inject constructor( return } - executors.others().execute { - try { - val reconnect = preferences.getBoolean("firebase_reconnect_update", true) - firebaseAuthRepository.loginToFirebase(person, access, reconnect) - preferences.edit().putBoolean("firebase_reconnect_update", false).apply() - } catch (t: Throwable) { - Timber.e(t) - } - } - var result = 0 var skipped = 0 diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/repository/SnowpiercerLoginRepository.kt b/app/src/main/java/com/forcetower/uefs/core/storage/repository/SnowpiercerLoginRepository.kt index 3c730794d..94732468c 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/repository/SnowpiercerLoginRepository.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/repository/SnowpiercerLoginRepository.kt @@ -47,7 +47,6 @@ class SnowpiercerLoginRepository @Inject constructor( private val database: UDatabase, private val context: Context, private val executors: AppExecutors, - private val firebaseAuthRepository: FirebaseAuthRepository, @Named("webViewUA") private val agent: String ) { val currentStep: MutableLiveData = MutableLiveData() @@ -78,19 +77,6 @@ class SnowpiercerLoginRepository @Inject constructor( database.accessDao().insert(username, password) val person = (login as Outcome.Success).value - executors.others().execute { - firebaseAuthRepository.loginToFirebase( - SagresPerson( - person.id, - person.name, - person.name, - person.cpf, - person.email - ), - Access(username = username, password = password), - true - ) - } val localProfileId = defineUser(person) diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/repository/cloud/EdgeAccountRepository.kt b/app/src/main/java/com/forcetower/uefs/core/storage/repository/cloud/EdgeAccountRepository.kt new file mode 100644 index 000000000..7dec97da5 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/core/storage/repository/cloud/EdgeAccountRepository.kt @@ -0,0 +1,37 @@ +package com.forcetower.uefs.core.storage.repository.cloud + +import com.forcetower.uefs.core.model.edge.ChangePictureDTO +import com.forcetower.uefs.core.model.unes.EdgeServiceAccount +import com.forcetower.uefs.core.storage.database.UDatabase +import com.forcetower.uefs.core.storage.network.EdgeService +import dagger.Reusable +import timber.log.Timber +import javax.inject.Inject + +@Reusable +class EdgeAccountRepository @Inject constructor( + private val service: EdgeService, + private val database: UDatabase +) { + fun getAccount() = database.edgeServiceAccount.me() + + suspend fun fetchAccountIfNeeded() { + val token = database.edgeAccessToken.require() ?: return + Timber.d("Has edge token $token") + + val me = service.me().data + val value = EdgeServiceAccount( + id = me.id, + name = me.name, + email = me.email, + imageUrl = me.imageUrl, + me = true + ) + database.edgeServiceAccount.insertOrUpdate(value) + } + + suspend fun uploadPicture(base64: String) { + service.uploadPicture(ChangePictureDTO(base64)) + runCatching { fetchAccountIfNeeded() } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/core/storage/repository/cloud/EdgeAuthRepository.kt b/app/src/main/java/com/forcetower/uefs/core/storage/repository/cloud/EdgeAuthRepository.kt index cfd416fcf..162c000a3 100644 --- a/app/src/main/java/com/forcetower/uefs/core/storage/repository/cloud/EdgeAuthRepository.kt +++ b/app/src/main/java/com/forcetower/uefs/core/storage/repository/cloud/EdgeAuthRepository.kt @@ -2,8 +2,16 @@ package com.forcetower.uefs.core.storage.repository.cloud import com.forcetower.uefs.core.model.edge.AssertionData import com.forcetower.uefs.core.model.edge.CompleteAssertionData +import com.forcetower.uefs.core.model.edge.EdgeLoginBody +import com.forcetower.uefs.core.model.edge.EmailLinkBodyDTO +import com.forcetower.uefs.core.model.edge.EmailLinkConfirmDTO import com.forcetower.uefs.core.model.edge.RegisterPasskeyCredential import com.forcetower.uefs.core.model.edge.RegisterPasskeyStart +import com.forcetower.uefs.core.model.ui.edge.EmailLinkComplete +import com.forcetower.uefs.core.model.ui.edge.EmailLinkStart +import com.forcetower.uefs.core.model.unes.EdgeAccessToken +import com.forcetower.uefs.core.model.unes.EdgeServiceAccount +import com.forcetower.uefs.core.storage.database.UDatabase import com.forcetower.uefs.core.storage.network.EdgeService import timber.log.Timber import javax.inject.Inject @@ -11,22 +19,119 @@ import javax.inject.Singleton @Singleton class EdgeAuthRepository @Inject constructor( - private val service: EdgeService + private val service: EdgeService, + private val database: UDatabase ) { + suspend fun anonymous(username: String, password: String) { + val result = service.loginAnonymous(EdgeLoginBody(username, password)) + database.edgeAccessToken.insert(EdgeAccessToken(result.accessToken)) + } + suspend fun startAssertion(): AssertionData { return service.startAssertion() } - suspend fun completeAssertion(flowId: String, response: String) { + suspend fun completeAssertion(flowId: String, response: String): EdgeServiceAccount? { val token = service.completeAssertion(CompleteAssertionData(flowId, response)) - Timber.d("Token $token") + database.edgeAccessToken.insert(EdgeAccessToken(token.accessToken)) + + runCatching { + val me = service.me().data + val value = EdgeServiceAccount( + id = me.id, + name = me.name, + email = me.email, + imageUrl = me.imageUrl, + me = true + ) + database.edgeServiceAccount.insertOrUpdate(value) + } + + return database.edgeServiceAccount.requireMe() } - suspend fun registerStart(): RegisterPasskeyStart { + suspend fun passkeyRegisterStart(): RegisterPasskeyStart { return service.registerPasskeyStart() } - suspend fun registerFinish(flowId: String, credential: String) { + suspend fun passkeyRegisterFinish(flowId: String, credential: String) { return service.registerPasskeyFinish(RegisterPasskeyCredential(flowId, credential)) } + + suspend fun emailLinkStart(email: String): EmailLinkStart { + try { + val response = service.linkEmailStart(EmailLinkBodyDTO(email)) + val body = response.body() + if (body != null && response.isSuccessful) + return EmailLinkStart.CodeSent(body.data.securityToken) + + return EmailLinkStart.InvalidInfo + } catch (error: Throwable) { + return EmailLinkStart.ConnectionError + } + } + + suspend fun emailLinkFinish(code: String, securityToken: String): EmailLinkComplete { + try { + val response = service.linkEmailComplete(EmailLinkConfirmDTO(code = code, securityToken = securityToken)) + if (response.isSuccessful) return EmailLinkComplete.Linked + if (response.code() == 400) return EmailLinkComplete.InvalidCode + if (response.code() == 409) return EmailLinkComplete.EmailTaken + if (response.code() == 429) return EmailLinkComplete.TooManyTries + + return EmailLinkComplete.ConnectionError + } catch (error: Throwable) { + Timber.e(error, "Failed to finish registration!") + return EmailLinkComplete.ConnectionError + } + } + + suspend fun prepareAndLogin() { + val current = database.edgeAccessToken.require() + if (current != null) { + Timber.i("Current access already exists!") + return + } + + val access = database.accessDao().getAccessDirectSuspend() + if (access == null) { + Timber.i("Access is null! How?") + return + } + + if (!access.valid) { + Timber.i("Access is not valid. Skipping!") + return + } + + val result = service.loginAnonymous(EdgeLoginBody(access.username, access.password)) + Timber.i("Login completed with result $result") + database.edgeAccessToken.insert(EdgeAccessToken(result.accessToken)) + } + + suspend fun doAnonymousLogin(): EdgeServiceAccount? { + val access = database.accessDao().getAccessDirectSuspend() ?: throw IllegalStateException("Access is null!") + + if (!access.valid) { + throw IllegalStateException("Access is not in a valid state") + } + + val result = service.loginAnonymous(EdgeLoginBody(access.username, access.password)) + Timber.i("Login completed with result $result") + database.edgeAccessToken.insert(EdgeAccessToken(result.accessToken)) + + runCatching { + val me = service.me().data + val value = EdgeServiceAccount( + id = me.id, + name = me.name, + email = me.email, + imageUrl = me.imageUrl, + me = true + ) + database.edgeServiceAccount.insertOrUpdate(value) + } + + return database.edgeServiceAccount.requireMe() + } } diff --git a/app/src/main/java/com/forcetower/uefs/core/task/definers/DisciplinesProcessor.kt b/app/src/main/java/com/forcetower/uefs/core/task/definers/DisciplinesProcessor.kt index da1ecd682..12c409777 100644 --- a/app/src/main/java/com/forcetower/uefs/core/task/definers/DisciplinesProcessor.kt +++ b/app/src/main/java/com/forcetower/uefs/core/task/definers/DisciplinesProcessor.kt @@ -25,6 +25,7 @@ import androidx.room.withTransaction import com.forcetower.core.extensions.removeSeconds import com.forcetower.uefs.core.model.unes.Class import com.forcetower.uefs.core.model.unes.ClassGroup +import com.forcetower.uefs.core.model.unes.ClassGroupTeacher import com.forcetower.uefs.core.model.unes.ClassLocation import com.forcetower.uefs.core.model.unes.Discipline import com.forcetower.uefs.core.model.unes.Teacher @@ -72,18 +73,25 @@ class DisciplinesProcessor( val classId = database.classDao().insertNewWays(bound) it.classes.forEach { clazz -> - val teacherId = insertTeacher(clazz.teacher, it.department) val group = ClassGroup( classId = classId, credits = clazz.hours, draft = false, group = clazz.groupName, - teacher = clazz.teacher?.name?.toTitleCase(), + teacher = clazz.teachers.mapNotNull { t -> t.name.toTitleCase() }.joinToString(", "), sagresId = clazz.id, - teacherId = teacherId, - teacherEmail = clazz.teacher?.email + teacherId = null, + teacherEmail = null ) val groupId = database.classGroupDao().insertNewWay(group) + + if (clazz.teachers.isNotEmpty()) + database.classGroupTeacher().deleteAllFromClassGroup(groupId) + + clazz.teachers.forEach { teacher -> + val id = insertTeacher(teacher, it.department) + database.classGroupTeacher().insert(ClassGroupTeacher(0, classGroupId = groupId, teacherId = id)) + } if (currentSemester?.uid == semesterId) { clazz.allocations.forEach { allocation -> val time = allocation.time @@ -129,8 +137,7 @@ class DisciplinesProcessor( } } - private suspend fun insertTeacher(teacher: Person?, department: String?): Long? { - teacher ?: return null + private suspend fun insertTeacher(teacher: Person, department: String?): Long { val value = Teacher( 0, teacher.name, diff --git a/app/src/main/java/com/forcetower/uefs/domain/usecase/account/ChangeProfilePictureUseCase.kt b/app/src/main/java/com/forcetower/uefs/domain/usecase/account/ChangeProfilePictureUseCase.kt new file mode 100644 index 000000000..1fbf9be28 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/domain/usecase/account/ChangeProfilePictureUseCase.kt @@ -0,0 +1,39 @@ +package com.forcetower.uefs.domain.usecase.account + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.media.ThumbnailUtils +import android.net.Uri +import android.util.Base64 +import com.forcetower.uefs.core.storage.repository.cloud.EdgeAccountRepository +import dagger.Reusable +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.ByteArrayOutputStream +import javax.inject.Inject + +@Reusable +class ChangeProfilePictureUseCase @Inject constructor( + private val repository: EdgeAccountRepository, + @ApplicationContext private val context: Context +) { + suspend operator fun invoke(uri: Uri) = withContext(Dispatchers.IO) { + val resolver = context.contentResolver + val stream = resolver.openInputStream(uri) ?: return@withContext + + val image = BitmapFactory.decodeStream(stream) + image ?: return@withContext + + val bitmap = ThumbnailUtils.extractThumbnail(image, 1080, 1080) + + val baos = ByteArrayOutputStream() + bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos) + val data = baos.toByteArray() + + val encoded = Base64.encodeToString(data, Base64.DEFAULT) + + repository.uploadPicture(encoded) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/domain/usecase/account/GetEdgeServiceAccountUseCase.kt b/app/src/main/java/com/forcetower/uefs/domain/usecase/account/GetEdgeServiceAccountUseCase.kt new file mode 100644 index 000000000..39753662b --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/domain/usecase/account/GetEdgeServiceAccountUseCase.kt @@ -0,0 +1,13 @@ +package com.forcetower.uefs.domain.usecase.account + +import com.forcetower.uefs.core.storage.repository.cloud.EdgeAccountRepository +import dagger.Reusable +import javax.inject.Inject + +@Reusable +class GetEdgeServiceAccountUseCase @Inject constructor( + private val repository: EdgeAccountRepository +) { + operator fun invoke() = repository.getAccount() + suspend fun update() = repository.fetchAccountIfNeeded() +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/domain/usecase/CompleteAssertionUseCase.kt b/app/src/main/java/com/forcetower/uefs/domain/usecase/auth/CompleteAssertionUseCase.kt similarity index 66% rename from app/src/main/java/com/forcetower/uefs/domain/usecase/CompleteAssertionUseCase.kt rename to app/src/main/java/com/forcetower/uefs/domain/usecase/auth/CompleteAssertionUseCase.kt index 38412eef4..4a3c2c4c1 100644 --- a/app/src/main/java/com/forcetower/uefs/domain/usecase/CompleteAssertionUseCase.kt +++ b/app/src/main/java/com/forcetower/uefs/domain/usecase/auth/CompleteAssertionUseCase.kt @@ -1,5 +1,6 @@ -package com.forcetower.uefs.domain.usecase +package com.forcetower.uefs.domain.usecase.auth +import com.forcetower.uefs.core.model.unes.EdgeServiceAccount import com.forcetower.uefs.core.storage.repository.cloud.EdgeAuthRepository import dagger.Reusable import timber.log.Timber @@ -9,8 +10,8 @@ import javax.inject.Inject class CompleteAssertionUseCase @Inject constructor( private val auth: EdgeAuthRepository ) { - suspend operator fun invoke(flowId: String, response: String) { + suspend operator fun invoke(flowId: String, response: String): EdgeServiceAccount? { Timber.d("Credential: $response") - auth.completeAssertion(flowId, response) + return auth.completeAssertion(flowId, response) } } diff --git a/app/src/main/java/com/forcetower/uefs/domain/usecase/auth/EdgeAnonymousLoginUseCase.kt b/app/src/main/java/com/forcetower/uefs/domain/usecase/auth/EdgeAnonymousLoginUseCase.kt new file mode 100644 index 000000000..6358ede43 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/domain/usecase/auth/EdgeAnonymousLoginUseCase.kt @@ -0,0 +1,23 @@ +package com.forcetower.uefs.domain.usecase.auth + +import com.forcetower.uefs.core.model.unes.EdgeServiceAccount +import com.forcetower.uefs.core.storage.repository.cloud.EdgeAuthRepository +import dagger.Reusable +import javax.inject.Inject + +@Reusable +class EdgeAnonymousLoginUseCase @Inject constructor( + private val repository: EdgeAuthRepository +) { + suspend fun prepareAndLogin() { + return repository.prepareAndLogin() + } + + suspend fun loginOrThrow(): EdgeServiceAccount? { + return repository.doAnonymousLogin() + } + + suspend fun invoke(username: String, password: String) { + repository.anonymous(username, password) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/domain/usecase/auth/LinkEmailUseCase.kt b/app/src/main/java/com/forcetower/uefs/domain/usecase/auth/LinkEmailUseCase.kt new file mode 100644 index 000000000..2e703a67d --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/domain/usecase/auth/LinkEmailUseCase.kt @@ -0,0 +1,20 @@ +package com.forcetower.uefs.domain.usecase.auth + +import com.forcetower.uefs.core.model.ui.edge.EmailLinkComplete +import com.forcetower.uefs.core.model.ui.edge.EmailLinkStart +import com.forcetower.uefs.core.storage.repository.cloud.EdgeAuthRepository +import dagger.Reusable +import javax.inject.Inject + +@Reusable +class LinkEmailUseCase @Inject constructor( + private val repository: EdgeAuthRepository +) { + suspend fun start(email: String): EmailLinkStart { + return repository.emailLinkStart(email) + } + + suspend fun finish(code: String, securityToken: String): EmailLinkComplete { + return repository.emailLinkFinish(code, securityToken) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/domain/usecase/RegisterPasskeyUseCase.kt b/app/src/main/java/com/forcetower/uefs/domain/usecase/auth/RegisterPasskeyUseCase.kt similarity index 78% rename from app/src/main/java/com/forcetower/uefs/domain/usecase/RegisterPasskeyUseCase.kt rename to app/src/main/java/com/forcetower/uefs/domain/usecase/auth/RegisterPasskeyUseCase.kt index 4ff371a66..acb96f128 100644 --- a/app/src/main/java/com/forcetower/uefs/domain/usecase/RegisterPasskeyUseCase.kt +++ b/app/src/main/java/com/forcetower/uefs/domain/usecase/auth/RegisterPasskeyUseCase.kt @@ -1,4 +1,4 @@ -package com.forcetower.uefs.domain.usecase +package com.forcetower.uefs.domain.usecase.auth import com.forcetower.uefs.core.model.edge.PasskeyRegister import com.forcetower.uefs.core.model.edge.RegisterPasskeyStart @@ -14,13 +14,12 @@ class RegisterPasskeyUseCase @Inject constructor( private val gson: Gson ) { suspend fun start(): RegisterPasskeyStart { - val data = edge.registerStart() - Timber.d("Original data: ${data.create}") + val data = edge.passkeyRegisterStart() val register = gson.fromJson(data.create, PasskeyRegister::class.java) return data.copy(create = gson.toJson(register.publicKey)) } suspend fun finish(flowId: String, credential: String) { - return edge.registerFinish(flowId, credential) + return edge.passkeyRegisterFinish(flowId, credential) } } diff --git a/app/src/main/java/com/forcetower/uefs/domain/usecase/StartAssertionUseCase.kt b/app/src/main/java/com/forcetower/uefs/domain/usecase/auth/StartAssertionUseCase.kt similarity index 93% rename from app/src/main/java/com/forcetower/uefs/domain/usecase/StartAssertionUseCase.kt rename to app/src/main/java/com/forcetower/uefs/domain/usecase/auth/StartAssertionUseCase.kt index e85bf3bac..7e108cb58 100644 --- a/app/src/main/java/com/forcetower/uefs/domain/usecase/StartAssertionUseCase.kt +++ b/app/src/main/java/com/forcetower/uefs/domain/usecase/auth/StartAssertionUseCase.kt @@ -1,4 +1,4 @@ -package com.forcetower.uefs.domain.usecase +package com.forcetower.uefs.domain.usecase.auth import com.forcetower.uefs.core.model.edge.AssertionData import com.forcetower.uefs.core.model.edge.PasskeyAssert diff --git a/app/src/main/java/com/forcetower/uefs/domain/usecase/profile/GetProfileUseCase.kt b/app/src/main/java/com/forcetower/uefs/domain/usecase/profile/GetProfileUseCase.kt new file mode 100644 index 000000000..459d32113 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/domain/usecase/profile/GetProfileUseCase.kt @@ -0,0 +1,12 @@ +package com.forcetower.uefs.domain.usecase.profile + +import com.forcetower.uefs.core.storage.repository.ProfileRepository +import dagger.Reusable +import javax.inject.Inject + +@Reusable +class GetProfileUseCase @Inject constructor( + private val repository: ProfileRepository +) { + operator fun invoke() = repository.me() +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/about/AboutMeFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/about/AboutMeFragment.kt index 646526291..9b9a78a1a 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/about/AboutMeFragment.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/about/AboutMeFragment.kt @@ -30,9 +30,9 @@ import android.text.style.AlignmentSpan import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import com.bumptech.glide.Glide import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions import com.forcetower.uefs.BuildConfig -import com.forcetower.uefs.GlideApp import com.forcetower.uefs.R import com.forcetower.uefs.core.util.HtmlUtils import com.forcetower.uefs.databinding.FragmentAboutMeBinding @@ -71,10 +71,10 @@ class AboutMeFragment : UFragment() { val sequence = TextUtils.concat(about1, "\n", about2, "\n\n", aboutSupport, "\n\n", about3, "\n", about4) HtmlUtils.setTextWithNiceLinks(binding.textAboutContinuation, sequence) - GlideApp.with(this) + Glide.with(this) .load("https://avatars.githubusercontent.com/ForceTower") - .fallback(R.mipmap.ic_unes_large_image_512) - .placeholder(R.mipmap.ic_unes_large_image_512) + .fallback(com.forcetower.core.R.mipmap.ic_unes_large_image_512) + .placeholder(com.forcetower.core.R.mipmap.ic_unes_large_image_512) .transition(DrawableTransitionOptions.withCrossFade()) .circleCrop() .into(binding.imageCreatorPicture) diff --git a/app/src/main/java/com/forcetower/uefs/feature/about/ContributorsFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/about/ContributorsFragment.kt index 55e62057d..b58781b14 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/about/ContributorsFragment.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/about/ContributorsFragment.kt @@ -92,7 +92,7 @@ class ContributorsFragment : UFragment() { .setDefaultColorSchemeParams( CustomTabColorSchemeParams .Builder() - .setToolbarColor(ViewUtils.attributeColorUtils(requireContext(), R.attr.colorPrimary)) + .setToolbarColor(ViewUtils.attributeColorUtils(requireContext(), androidx.appcompat.R.attr.colorPrimary)) .build() ) .setShareState(CustomTabsIntent.SHARE_STATE_ON) diff --git a/app/src/main/java/com/forcetower/uefs/feature/adventure/AdventureFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/adventure/AdventureFragment.kt index 0ac99dbff..6f3a03013 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/adventure/AdventureFragment.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/adventure/AdventureFragment.kt @@ -55,8 +55,6 @@ import com.google.android.gms.location.LocationResult import com.google.android.gms.location.LocationServices import com.google.android.gms.location.LocationSettingsRequest import com.google.android.gms.location.Priority -import com.google.firebase.auth.FirebaseAuth -import com.google.firebase.storage.FirebaseStorage import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import timber.log.Timber @@ -64,8 +62,6 @@ import javax.inject.Inject @AndroidEntryPoint class AdventureFragment : UFragment() { - @Inject lateinit var firebaseAuth: FirebaseAuth - @Inject lateinit var firebaseStorage: FirebaseStorage @Inject lateinit var preferences: SharedPreferences private val viewModel: AdventureViewModel by activityViewModels() diff --git a/app/src/main/java/com/forcetower/uefs/feature/demand/DemandBindingAdapter.kt b/app/src/main/java/com/forcetower/uefs/feature/demand/DemandBindingAdapter.kt index 6f37a7408..766a8cd7f 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/demand/DemandBindingAdapter.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/demand/DemandBindingAdapter.kt @@ -23,8 +23,8 @@ package com.forcetower.uefs.feature.demand import android.widget.ImageView import androidx.core.content.ContextCompat import androidx.databinding.BindingAdapter +import com.bumptech.glide.Glide import com.forcetower.sagres.database.model.SagresDemandOffer -import com.forcetower.uefs.GlideApp import com.forcetower.uefs.R import com.google.android.material.card.MaterialCardView @@ -42,7 +42,7 @@ fun disciplineIcon(iv: ImageView, offer: SagresDemandOffer?) { else -> R.drawable.ic_bug_report_black_24dp } - GlideApp.with(iv.context).load(drawable).fitCenter().into(iv) + Glide.with(iv.context).load(drawable).fitCenter().into(iv) } @BindingAdapter(value = ["animatedStrokeColor"]) diff --git a/app/src/main/java/com/forcetower/uefs/feature/disciplines/DisciplineViewModel.kt b/app/src/main/java/com/forcetower/uefs/feature/disciplines/DisciplineViewModel.kt index fcb755a64..064147717 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/disciplines/DisciplineViewModel.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/disciplines/DisciplineViewModel.kt @@ -36,6 +36,7 @@ import com.forcetower.uefs.core.model.unes.ClassItem import com.forcetower.uefs.core.model.unes.ClassLocation import com.forcetower.uefs.core.model.unes.ClassMaterial import com.forcetower.uefs.core.storage.database.aggregation.ClassFullWithGroup +import com.forcetower.uefs.core.storage.database.aggregation.ClassGroupWithTeachers import com.forcetower.uefs.core.storage.repository.DisciplineDetailsRepository import com.forcetower.uefs.core.storage.repository.DisciplinesRepository import com.forcetower.uefs.core.storage.repository.SagresGradesRepository @@ -67,8 +68,8 @@ class DisciplineViewModel @Inject constructor( val clazz: LiveData get() = _classFull - private val _group = MediatorLiveData() - val group: LiveData + private val _group = MediatorLiveData() + val group: LiveData get() = _group private val _absences = MediatorLiveData>() @@ -155,7 +156,7 @@ class DisciplineViewModel @Inject constructor( if (it != null) { val source = repository.getClassGroup(it) _group.addSource(source) { value -> - _group.value = value?.group + _group.value = value?.let { el -> ClassGroupWithTeachers(el.group, el.teachers) } } } } diff --git a/app/src/main/java/com/forcetower/uefs/feature/disciplines/disciplinedetail/overview/OverviewAdapter.kt b/app/src/main/java/com/forcetower/uefs/feature/disciplines/disciplinedetail/overview/OverviewAdapter.kt index c570201f3..4e225fae5 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/disciplines/disciplinedetail/overview/OverviewAdapter.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/disciplines/disciplinedetail/overview/OverviewAdapter.kt @@ -29,12 +29,15 @@ import androidx.recyclerview.widget.RecyclerView import com.forcetower.uefs.R import com.forcetower.uefs.core.model.unes.ClassGroup import com.forcetower.uefs.core.model.unes.ClassLocation +import com.forcetower.uefs.core.model.unes.Teacher import com.forcetower.uefs.core.storage.database.aggregation.ClassFullWithGroup +import com.forcetower.uefs.core.storage.database.aggregation.ClassGroupWithTeachers import com.forcetower.uefs.databinding.ItemDisciplineGoalsBinding import com.forcetower.uefs.databinding.ItemDisciplineScheduleHideBinding import com.forcetower.uefs.databinding.ItemDisciplineShortBinding import com.forcetower.uefs.databinding.ItemDisciplineTeacherBinding import com.forcetower.uefs.feature.disciplines.DisciplineViewModel +import com.forcetower.uefs.feature.shared.extensions.toTitleCase import com.forcetower.uefs.feature.shared.inflate import com.forcetower.uefs.feature.shared.inflater @@ -48,7 +51,7 @@ class OverviewAdapter( differ.submitList(buildMergedList(clazz = value)) } - var currentGroup: ClassGroup? = null + var currentGroup: ClassGroupWithTeachers? = null set(value) { field = value differ.submitList(buildMergedList(group = value)) @@ -82,6 +85,7 @@ class OverviewAdapter( is OverviewHolder.TeacherHolder -> holder.binding.apply { lifecycleOwner = this@OverviewAdapter.lifecycleOwner viewModel = this@OverviewAdapter.viewModel + name = (differ.currentList[position] as? DisciplineTeacher)?.teacher executePendingBindings() } is OverviewHolder.ResumeHolder -> holder.binding.apply { @@ -115,12 +119,12 @@ class OverviewAdapter( private fun buildMergedList( clazz: ClassFullWithGroup? = currentClazz, - group: ClassGroup? = currentGroup, + group: ClassGroupWithTeachers? = currentGroup, schedule: List = currentSchedule ): List { val list = mutableListOf() if (clazz != null) { - if (group?.draft == true) { + if (group?.data?.draft == true) { list += DisciplineDraft } @@ -128,8 +132,10 @@ class OverviewAdapter( list += DisciplineShort } - if (group?.teacher != null) { - list += DisciplineTeacher + group?.teachers?.forEach { t -> + t.name.toTitleCase()?.let { + list += DisciplineTeacher(t.uid, it) + } } if (clazz.discipline.resume != null) { @@ -146,7 +152,7 @@ class OverviewAdapter( return list } - private val differ = AsyncListDiffer(this, DiffCallback) + private val differ = AsyncListDiffer(this, DiffCallback) } sealed class OverviewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { @@ -162,10 +168,10 @@ private object DiffCallback : DiffUtil.ItemCallback() { return when { oldItem === DisciplineDraft && newItem === DisciplineDraft -> true oldItem === DisciplineShort && newItem === DisciplineShort -> true - oldItem === DisciplineTeacher && newItem === DisciplineTeacher -> true oldItem === DisciplineResume && newItem === DisciplineResume -> true oldItem === Statistics && newItem === Statistics -> true oldItem === ScheduleHeader && newItem === ScheduleHeader -> true + oldItem is DisciplineTeacher && newItem is DisciplineTeacher -> oldItem.id == newItem.id oldItem is ClassLocation && newItem is ClassLocation -> oldItem.uid == newItem.uid else -> false } @@ -174,6 +180,7 @@ private object DiffCallback : DiffUtil.ItemCallback() { override fun areContentsTheSame(oldItem: Any, newItem: Any): Boolean { return when { oldItem is ClassLocation && newItem is ClassLocation -> oldItem == newItem + oldItem is DisciplineTeacher && newItem is DisciplineTeacher -> oldItem == newItem else -> true } } @@ -181,7 +188,10 @@ private object DiffCallback : DiffUtil.ItemCallback() { private object DisciplineDraft private object DisciplineShort -private object DisciplineTeacher +private data class DisciplineTeacher( + val id: Long, + val teacher: String +) private object DisciplineResume private object Statistics private object ScheduleHeader diff --git a/app/src/main/java/com/forcetower/uefs/feature/disciplines/disciplinedetail/overview/OverviewFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/disciplines/disciplinedetail/overview/OverviewFragment.kt index 96d7c8cf8..ad98f53d7 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/disciplines/disciplinedetail/overview/OverviewFragment.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/disciplines/disciplinedetail/overview/OverviewFragment.kt @@ -38,7 +38,7 @@ class OverviewFragment : UFragment() { private lateinit var binding: FragmentDisciplineOverviewBinding private val viewModel: DisciplineViewModel by activityViewModels() - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { return FragmentDisciplineOverviewBinding.inflate(inflater, container, false).also { binding = it }.root @@ -56,9 +56,9 @@ class OverviewFragment : UFragment() { } } - viewModel.clazz.observe(viewLifecycleOwner, Observer { overviewAdapter.currentClazz = it }) - viewModel.group.observe(viewLifecycleOwner, Observer { overviewAdapter.currentGroup = it }) - viewModel.schedule.observe(viewLifecycleOwner, Observer { overviewAdapter.currentSchedule = it }) + viewModel.clazz.observe(viewLifecycleOwner) { overviewAdapter.currentClazz = it } + viewModel.group.observe(viewLifecycleOwner) { overviewAdapter.currentGroup = it } + viewModel.schedule.observe(viewLifecycleOwner) { overviewAdapter.currentSchedule = it } } companion object { diff --git a/app/src/main/java/com/forcetower/uefs/feature/evaluation/EvaluationBindingAdapters.kt b/app/src/main/java/com/forcetower/uefs/feature/evaluation/EvaluationBindingAdapters.kt index 2de51ce61..84ea6352b 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/evaluation/EvaluationBindingAdapters.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/evaluation/EvaluationBindingAdapters.kt @@ -60,7 +60,7 @@ fun formatSemesterGradeChart(chart: LineChart, list: List?) { set.setDrawFilled(true) set.fillDrawable = ContextCompat.getDrawable(context, R.drawable.gradient_chart_evaluation) set.mode = LineDataSet.Mode.CUBIC_BEZIER - set.color = ViewUtils.attributeColorUtils(context, R.attr.colorAccent) + set.color = ViewUtils.attributeColorUtils(context, androidx.appcompat.R.attr.colorAccent) set.setDrawCircles(false) set.setDrawValues(false) @@ -76,7 +76,7 @@ fun formatSemesterGradeChart(chart: LineChart, list: List?) { val typedValue = TypedValue() val theme = context.theme - theme.resolveAttribute(R.attr.colorOnSurface, typedValue, true) + theme.resolveAttribute(com.google.android.material.R.attr.colorOnSurface, typedValue, true) val colorOnSurface = typedValue.data chart.apply { diff --git a/app/src/main/java/com/forcetower/uefs/feature/feedback/FeedbackViewModel.kt b/app/src/main/java/com/forcetower/uefs/feature/feedback/FeedbackViewModel.kt index 736ac841d..8584bbfcf 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/feedback/FeedbackViewModel.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/feedback/FeedbackViewModel.kt @@ -35,7 +35,6 @@ import javax.inject.Inject @HiltViewModel class FeedbackViewModel @Inject constructor( private val repository: FeedbackRepository, - private val context: Context ) : ViewModel() { private val _sendFeedback = MediatorLiveData>() val sendFeedback: LiveData> @@ -47,15 +46,6 @@ class FeedbackViewModel @Inject constructor( @MainThread fun onSendFeedback(text: String?) { - if (text.isNullOrBlank()) { - _textError.value = Event(context.getString(R.string.feedback_text_empty)) - } else { - _textError.value = Event(null) - val source = repository.onSendFeedback(text) - _sendFeedback.addSource(source) { - _sendFeedback.value = Event(it) - _sendFeedback.removeSource(source) - } - } + } } diff --git a/app/src/main/java/com/forcetower/uefs/feature/flowchart/FlowchartBindingAdapters.kt b/app/src/main/java/com/forcetower/uefs/feature/flowchart/FlowchartBindingAdapters.kt index 94ebb8169..406216e5f 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/flowchart/FlowchartBindingAdapters.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/flowchart/FlowchartBindingAdapters.kt @@ -49,7 +49,7 @@ fun disciplineFlowchartTextColor(tv: TextView, completed: Boolean?, participatin val participate = participating ?: false val ctx = tv.context val color = when { - complete -> ViewUtils.attributeColorUtils(ctx, R.attr.colorPrimary) + complete -> ViewUtils.attributeColorUtils(ctx, androidx.appcompat.R.attr.colorPrimary) participate -> ContextCompat.getColor(ctx, R.color.yellow) else -> ContextCompat.getColor(ctx, R.color.red) } diff --git a/app/src/main/java/com/forcetower/uefs/feature/home/HomeActivity.kt b/app/src/main/java/com/forcetower/uefs/feature/home/HomeActivity.kt index 9c6aff4b3..6d479510d 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/home/HomeActivity.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/home/HomeActivity.kt @@ -74,7 +74,6 @@ import com.google.android.play.core.ktx.requestReview import com.google.android.play.core.review.ReviewManager import com.google.android.play.core.review.ReviewManagerFactory import com.google.firebase.analytics.FirebaseAnalytics -import com.google.firebase.auth.FirebaseAuth import com.google.firebase.remoteconfig.FirebaseRemoteConfig import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.delay @@ -84,7 +83,6 @@ import javax.inject.Inject @AndroidEntryPoint class HomeActivity : UGameActivity() { - @Inject lateinit var firebaseAuth: FirebaseAuth @Inject lateinit var preferences: SharedPreferences @Inject lateinit var analytics: FirebaseAnalytics @Inject lateinit var remoteConfig: FirebaseRemoteConfig @@ -264,9 +262,7 @@ class HomeActivity : UGameActivity() { viewModel.accessToken.observe(this) { onAccessTokenUpdate(it) } viewModel.snackbarMessage.observe(this, EventObserver { showSnack(it) }) dynamicDFMViewModel.snackbarMessage.observe(this, EventObserver { showSnack(it) }) - viewModel.sendToken().observe(this) { - Timber.d("Sent token... I guess ($it)") - } + viewModel.sendToken() if (preferences.isStudentFromUEFS()) { // Update and unlock achievements for participating in a class with the creator viewModel.connectToServiceIfNeeded() @@ -299,7 +295,6 @@ class HomeActivity : UGameActivity() { private fun onAccessUpdate(access: Access?) { if (access == null) { Timber.d("Access Invalidated") - firebaseAuth.signOut() val intent = Intent(this, LoginActivity::class.java) intent.addFlags(FLAG_ACTIVITY_NEW_TASK) intent.addFlags(FLAG_ACTIVITY_CLEAR_TOP) diff --git a/app/src/main/java/com/forcetower/uefs/feature/home/HomeBottomFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/home/HomeBottomFragment.kt index 71aacaeef..b67a0c455 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/home/HomeBottomFragment.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/home/HomeBottomFragment.kt @@ -32,6 +32,7 @@ import androidx.core.content.ContextCompat import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import androidx.navigation.ui.NavigationUI +import com.bumptech.glide.Glide import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions import com.canhub.cropper.CropImageContract import com.canhub.cropper.CropImageContractOptions @@ -39,10 +40,10 @@ import com.canhub.cropper.CropImageOptions import com.canhub.cropper.CropImageView import com.forcetower.core.utils.ColorUtils import com.forcetower.uefs.BuildConfig -import com.forcetower.uefs.GlideApp import com.forcetower.uefs.R import com.forcetower.uefs.core.model.unes.Account import com.forcetower.uefs.core.model.unes.Course +import com.forcetower.uefs.core.model.unes.EdgeServiceAccount import com.forcetower.uefs.core.util.isStudentFromUEFS import com.forcetower.uefs.databinding.HomeBottomBinding import com.forcetower.uefs.feature.about.AboutActivity @@ -80,7 +81,6 @@ class HomeBottomFragment : UFragment() { lifecycleOwner = this@HomeBottomFragment viewModel = this@HomeBottomFragment.viewModel executePendingBindings() - imageUserPicture.setOnClickListener { pickImage() } textUserName.setOnClickListener { editCourse() } textScore.setOnClickListener { editCourse() } }.root @@ -106,7 +106,7 @@ class HomeBottomFragment : UFragment() { viewModel.databaseAccount.observe(viewLifecycleOwner) { handleAccount(it) } } - private fun handleAccount(account: Account?) { + private fun handleAccount(account: EdgeServiceAccount?) { binding.account = account } @@ -188,10 +188,10 @@ class HomeBottomFragment : UFragment() { private fun onImagePicked(uri: Uri) { viewModel.setSelectedImage(uri) - GlideApp.with(requireContext()) + Glide.with(requireContext()) .load(uri) - .fallback(R.mipmap.ic_unes_large_image_512) - .placeholder(R.mipmap.ic_unes_large_image_512) + .fallback(com.forcetower.core.R.mipmap.ic_unes_large_image_512) + .placeholder(com.forcetower.core.R.mipmap.ic_unes_large_image_512) .circleCrop() .transition(DrawableTransitionOptions.withCrossFade()) .into(binding.imageUserPicture) diff --git a/app/src/main/java/com/forcetower/uefs/feature/home/HomeViewModel.kt b/app/src/main/java/com/forcetower/uefs/feature/home/HomeViewModel.kt index 3aabb6c08..d492888a2 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/home/HomeViewModel.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/home/HomeViewModel.kt @@ -27,6 +27,7 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import com.forcetower.core.lifecycle.Event import com.forcetower.uefs.core.model.unes.Access @@ -45,15 +46,18 @@ import com.forcetower.uefs.core.storage.repository.SagresDataRepository import com.forcetower.uefs.core.storage.repository.UserSessionRepository import com.forcetower.uefs.core.storage.repository.cloud.AffinityQuestionRepository import com.forcetower.uefs.core.storage.repository.cloud.AuthRepository +import com.forcetower.uefs.core.storage.repository.cloud.EdgeAccountRepository import com.forcetower.uefs.core.storage.resource.Resource import com.forcetower.uefs.core.storage.resource.Status import com.forcetower.uefs.core.task.FetchMissingSemestersUseCase import com.forcetower.uefs.core.work.image.UploadImageToStorage +import com.forcetower.uefs.domain.usecase.auth.EdgeAnonymousLoginUseCase import com.forcetower.uefs.easter.darktheme.DarkThemeRepository import com.google.android.play.core.install.model.AppUpdateType import com.google.android.play.core.install.model.InstallStatus import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject import javax.inject.Named @@ -68,10 +72,12 @@ class HomeViewModel @Inject constructor( application: Application, private val sessionRepository: UserSessionRepository, private val accountRepository: AccountRepository, + private val edgeAccountRepository: EdgeAccountRepository, private val affinityRepository: AffinityQuestionRepository, private val fetchMissingSemesters: FetchMissingSemestersUseCase, @Named("flagSnowpiercerEnabled") - private val snowpiercerEnabled: Boolean + private val snowpiercerEnabled: Boolean, + private val anonymousLoginUseCase: EdgeAnonymousLoginUseCase ) : AndroidViewModel(application) { private var selectImageUri: Uri? = null @@ -101,7 +107,7 @@ class HomeViewModel @Inject constructor( val semesters: LiveData> by lazy { dataRepository.getSemesters() } val course: LiveData by lazy { dataRepository.getCourse() } val account: LiveData> = accountRepository.getAccount() - val databaseAccount = accountRepository.getAccountOnDatabase() + val databaseAccount = edgeAccountRepository.getAccount().asLiveData() val flags: LiveData by lazy { dataRepository.getFlags() } val scheduleHideCount: LiveData = dataRepository.getScheduleHideCount() @@ -115,7 +121,12 @@ class HomeViewModel @Inject constructor( selectImageUri = uri } - fun logout() = dataRepository.logout() + fun logout() { + dataRepository.logout() + viewModelScope.launch { + dataRepository.logoutSuspend() + } + } fun showSnack(message: String) { _snackbar.value = Event(message) @@ -151,11 +162,19 @@ class HomeViewModel @Inject constructor( fun connectToServiceIfNeeded() { authRepository.performAccountSyncStateIfNeededAsync() + viewModelScope.launch { + runCatching { + anonymousLoginUseCase.prepareAndLogin() + }.onFailure { + Timber.e(it, "Failed to authenticate.") + } + } } - @MainThread - fun sendToken(): LiveData { - return firebaseMessageRepository.sendNewTokenOrNot() + fun sendToken() { + viewModelScope.launch { + firebaseMessageRepository.sendNewTokenOrNot() + } } fun setSelectedCourse(course: Course) { diff --git a/app/src/main/java/com/forcetower/uefs/feature/login/LoginFormViewModel.kt b/app/src/main/java/com/forcetower/uefs/feature/login/LoginFormViewModel.kt index b7bcdcccf..fd2fc0645 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/login/LoginFormViewModel.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/login/LoginFormViewModel.kt @@ -4,10 +4,10 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.forcetower.uefs.core.model.edge.RegisterPasskeyStart -import com.forcetower.uefs.domain.usecase.CompleteAssertionUseCase -import com.forcetower.uefs.domain.usecase.RegisterPasskeyUseCase -import com.forcetower.uefs.domain.usecase.StartAssertionUseCase -import com.forcetower.uefs.feature.shared.SingleLiveEvent +import com.forcetower.uefs.domain.usecase.auth.CompleteAssertionUseCase +import com.forcetower.uefs.domain.usecase.auth.RegisterPasskeyUseCase +import com.forcetower.uefs.domain.usecase.auth.StartAssertionUseCase +import com.forcetower.core.lifecycle.SingleLiveEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import timber.log.Timber diff --git a/app/src/main/java/com/forcetower/uefs/feature/login/SigningInFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/login/SigningInFragment.kt index 17a2b36cf..bb2b820ac 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/login/SigningInFragment.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/login/SigningInFragment.kt @@ -49,21 +49,12 @@ import com.forcetower.uefs.databinding.FragmentSigningInBinding import com.forcetower.uefs.feature.shared.UFragment import com.forcetower.uefs.feature.shared.fadeIn import com.forcetower.uefs.feature.shared.fadeOut -import com.google.firebase.auth.FirebaseAuth -import com.google.firebase.storage.FirebaseStorage import dagger.hilt.android.AndroidEntryPoint import timber.log.Timber import java.nio.charset.Charset -import javax.inject.Inject @AndroidEntryPoint class SigningInFragment : UFragment() { - @Inject - lateinit var firebaseAuth: FirebaseAuth - - @Inject - lateinit var firebaseStorage: FirebaseStorage - private lateinit var binding: FragmentSigningInBinding private val viewModel: LoginViewModel by viewModels() private lateinit var messages: Array @@ -71,20 +62,11 @@ class SigningInFragment : UFragment() { private val args by navArgs() - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - firebaseAuth = FirebaseAuth.getInstance() - } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { return FragmentSigningInBinding.inflate(inflater, container, false).also { binding = it prepareSwitcher() prepareMessages() - }.apply { - firebaseStorage = this@SigningInFragment.firebaseStorage - firebaseUser = this@SigningInFragment.firebaseAuth.currentUser - executePendingBindings() }.root } @@ -116,7 +98,7 @@ class SigningInFragment : UFragment() { val typedValue = TypedValue() val theme = requireContext().theme - theme.resolveAttribute(R.attr.colorOnSurface, typedValue, true) + theme.resolveAttribute(com.google.android.material.R.attr.colorOnSurface, typedValue, true) val colorOnSurface = typedValue.data textView.setTextColor(colorOnSurface) textView @@ -135,7 +117,7 @@ class SigningInFragment : UFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - viewModel.getLogin().observe(viewLifecycleOwner, { onLoginProgress(it) }) + viewModel.getLogin().observe(viewLifecycleOwner) { onLoginProgress(it) } viewModel.getProfile().observe(viewLifecycleOwner, Observer(this::onProfileUpdate)) viewModel.getStep(args.snowpiercer).observe(viewLifecycleOwner, Observer(this::onStep)) doLogin() @@ -144,7 +126,6 @@ class SigningInFragment : UFragment() { override fun onStart() { super.onStart() displayRandomText() - firebaseAuthListener() } private fun displayRandomText() { @@ -253,20 +234,8 @@ class SigningInFragment : UFragment() { ) } - private fun firebaseAuthListener() { - firebaseAuth.addAuthStateListener { - if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { - val current = it.currentUser - Timber.d("Auth State changed... User is ${current?.email}") - binding.firebaseUser = current - binding.executePendingBindings() - } - } - } - private fun snackAndBack(string: String) { showSnack(string) - firebaseAuth.signOut() binding.textHelloUser.text = "" binding.textHelloUser.fadeOut() binding.textTips.fadeOut() diff --git a/app/src/main/java/com/forcetower/uefs/feature/login/TechNopeCaptchaFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/login/TechNopeCaptchaFragment.kt index 8a5fd1b42..0c13af439 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/login/TechNopeCaptchaFragment.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/login/TechNopeCaptchaFragment.kt @@ -78,10 +78,7 @@ class TechNopeCaptchaFragment : UFragment() { @JavascriptInterface fun reCaptchaCallback(token: String) { Timber.d("reCaptcha token $token") - val directions = TechNopeCaptchaFragmentDirections.actionLoginTechNopeToLoginSigningIn(args.username, args.password, false).apply { - captchaToken = token - } - + val directions = TechNopeCaptchaFragmentDirections.actionLoginTechNopeToLoginSigningIn(args.username, args.password, false, captchaToken = token) requireActivity().runOnUiThread { findNavController().navigate(directions) } diff --git a/app/src/main/java/com/forcetower/uefs/feature/profile/ProfileBindingAdapters.kt b/app/src/main/java/com/forcetower/uefs/feature/profile/ProfileBindingAdapters.kt index 7ccaab40f..7861f1f18 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/profile/ProfileBindingAdapters.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/profile/ProfileBindingAdapters.kt @@ -25,13 +25,10 @@ import android.widget.ImageView import android.widget.TextView import androidx.databinding.BindingAdapter import androidx.preference.PreferenceManager +import com.bumptech.glide.Glide import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions -import com.bumptech.glide.signature.ObjectKey -import com.forcetower.uefs.GlideApp import com.forcetower.uefs.R import com.forcetower.uefs.core.model.unes.Semester -import com.google.firebase.auth.FirebaseUser -import com.google.firebase.storage.FirebaseStorage import java.text.SimpleDateFormat import java.time.ZoneOffset import java.time.ZonedDateTime @@ -45,41 +42,15 @@ import kotlin.math.min fun profileImage(iv: ImageView, url: String?) { if (url == null) return - GlideApp.with(iv.context) + Glide.with(iv.context) .load(url) - .fallback(R.mipmap.ic_unes_large_image_512) - .placeholder(R.mipmap.ic_unes_large_image_512) + .fallback(com.forcetower.core.R.mipmap.ic_unes_large_image_512) + .placeholder(com.forcetower.core.R.mipmap.ic_unes_large_image_512) .circleCrop() .transition(DrawableTransitionOptions.withCrossFade()) .into(iv) } -@BindingAdapter(requireAll = true, value = ["firebaseUser", "firebaseStorage"]) -fun firebaseUser(iv: ImageView, user: FirebaseUser?, storage: FirebaseStorage) { - if (user != null) { - val reference = storage.getReference("users/${user.uid}/avatar.jpg") - try { - GlideApp.with(iv.context) - .load(reference) - .fallback(R.mipmap.ic_unes_large_image_512) - .placeholder(R.mipmap.ic_unes_large_image_512) - .signature(ObjectKey(System.currentTimeMillis() ushr 21)) - .circleCrop() - .transition(DrawableTransitionOptions.withCrossFade()) - .into(iv) - } catch (ignored: Throwable) { - } - } else { - GlideApp.with(iv.context) - .load(R.mipmap.ic_unes_large_image_512) - .fallback(R.mipmap.ic_unes_large_image_512) - .placeholder(R.mipmap.ic_unes_large_image_512) - .circleCrop() - .transition(DrawableTransitionOptions.withCrossFade()) - .into(iv) - } -} - @BindingAdapter(value = ["profileScoreOptional", "profileScoreCalculated", "semestersList", "profileCourse"], requireAll = true) fun profileScoreOptional( tv: TextView, diff --git a/app/src/main/java/com/forcetower/uefs/feature/profile/ProfileFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/profile/ProfileFragment.kt index a8f27488c..6d3d4998a 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/profile/ProfileFragment.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/profile/ProfileFragment.kt @@ -48,15 +48,12 @@ import com.forcetower.uefs.feature.shared.extensions.inTransaction import com.forcetower.uefs.feature.shared.extensions.postponeEnterTransition import com.forcetower.uefs.feature.shared.getPixelsFromDp import com.forcetower.uefs.feature.siecomp.session.PushUpScrollListener -import com.google.firebase.auth.FirebaseAuth import com.google.firebase.storage.FirebaseStorage import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @AndroidEntryPoint class ProfileFragment : UFragment() { - @Inject lateinit var firebaseAuth: FirebaseAuth - @Inject lateinit var firebaseStorage: FirebaseStorage private val pickImageContract = registerForActivityResult(ActivityResultContracts.GetContent()) { onContentSelected(it) diff --git a/app/src/main/java/com/forcetower/uefs/feature/reminders/CreateReminderDialog.kt b/app/src/main/java/com/forcetower/uefs/feature/reminders/CreateReminderDialog.kt index 45e60c5f6..ff5784fb9 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/reminders/CreateReminderDialog.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/reminders/CreateReminderDialog.kt @@ -74,7 +74,7 @@ class CreateReminderDialog : RoundedDialog() { if (viewModel.currentDeadline != null) calendar.timeInMillis = viewModel.currentDeadline!! - val color = ViewUtils.attributeColorUtils(requireContext(), R.attr.colorPrimary) + val color = ViewUtils.attributeColorUtils(requireContext(), androidx.appcompat.R.attr.colorPrimary) val picker = DatePickerDialog.newInstance( { _, y, m, d -> diff --git a/app/src/main/java/com/forcetower/uefs/feature/settings/AdvancedSettingsFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/settings/AdvancedSettingsFragment.kt index ab0f0735b..97ad73b37 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/settings/AdvancedSettingsFragment.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/settings/AdvancedSettingsFragment.kt @@ -102,7 +102,7 @@ class AdvancedSettingsFragment : PreferenceFragmentCompat() { .setDefaultColorSchemeParams( CustomTabColorSchemeParams .Builder() - .setToolbarColor(ViewUtils.attributeColorUtils(requireContext(), R.attr.colorPrimary)) + .setToolbarColor(ViewUtils.attributeColorUtils(requireContext(), androidx.appcompat.R.attr.colorPrimary)) .build() ) .setShareState(CustomTabsIntent.SHARE_STATE_ON) diff --git a/app/src/main/java/com/forcetower/uefs/feature/settings/SyncSettingsFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/settings/SyncSettingsFragment.kt index bcb5cb401..af57c54ce 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/settings/SyncSettingsFragment.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/settings/SyncSettingsFragment.kt @@ -29,7 +29,6 @@ import androidx.preference.PreferenceFragmentCompat import androidx.preference.TwoStatePreference import com.forcetower.uefs.BuildConfig import com.forcetower.uefs.R -import com.forcetower.uefs.core.storage.repository.FirebaseAuthRepository import com.forcetower.uefs.core.storage.repository.SyncFrequencyRepository import com.forcetower.uefs.core.work.sync.SyncLinkedWorker import com.forcetower.uefs.core.work.sync.SyncMainWorker @@ -39,8 +38,6 @@ import javax.inject.Inject @AndroidEntryPoint class SyncSettingsFragment : PreferenceFragmentCompat() { - @Inject - lateinit var firebaseRepository: FirebaseAuthRepository @Inject lateinit var repository: SyncFrequencyRepository @@ -118,7 +115,6 @@ class SyncSettingsFragment : PreferenceFragmentCompat() { SyncLinkedWorker.createWorker(requireContext(), period) } } - firebaseRepository.updateFrequency(period) return true } @@ -139,8 +135,6 @@ class SyncSettingsFragment : PreferenceFragmentCompat() { SyncLinkedWorker.createWorker(requireContext(), period) getSharedPreferences()?.run { edit().putString("stg_sync_worker_type", "1").apply() } } - - firebaseRepository.updateFrequency(period) return true } diff --git a/app/src/main/java/com/forcetower/uefs/feature/setup/ConfigurationFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/setup/ConfigurationFragment.kt index f9d26aa66..c1f281b61 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/setup/ConfigurationFragment.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/setup/ConfigurationFragment.kt @@ -31,16 +31,12 @@ import com.forcetower.uefs.R import com.forcetower.uefs.core.model.service.SyncFrequency import com.forcetower.uefs.databinding.FragmentSetupConfigurationBinding import com.forcetower.uefs.feature.shared.UFragment -import com.google.firebase.auth.FirebaseAuth import com.judemanutd.autostarter.AutoStartPermissionHelper import dagger.hilt.android.AndroidEntryPoint import java.util.Locale -import javax.inject.Inject @AndroidEntryPoint class ConfigurationFragment : UFragment() { - @Inject lateinit var firebaseAuth: FirebaseAuth - private lateinit var binding: FragmentSetupConfigurationBinding private val viewModel: SetupViewModel by activityViewModels() diff --git a/app/src/main/java/com/forcetower/uefs/feature/setup/IntroductionFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/setup/IntroductionFragment.kt index e566f1bbd..3f6febd6f 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/setup/IntroductionFragment.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/setup/IntroductionFragment.kt @@ -30,14 +30,13 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController +import com.bumptech.glide.Glide import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions -import com.bumptech.glide.signature.ObjectKey import com.canhub.cropper.CropImageContract import com.canhub.cropper.CropImageContractOptions import com.canhub.cropper.CropImageOptions import com.canhub.cropper.CropImageView import com.forcetower.core.utils.ColorUtils -import com.forcetower.uefs.GlideApp import com.forcetower.uefs.R import com.forcetower.uefs.core.model.unes.Course import com.forcetower.uefs.core.storage.repository.SyncFrequencyRepository @@ -45,16 +44,11 @@ import com.forcetower.uefs.core.util.isStudentFromUEFS import com.forcetower.uefs.databinding.FragmentSetupIntroductionBinding import com.forcetower.uefs.feature.shared.UFragment import com.forcetower.uefs.feature.shared.getPixelsFromDp -import com.google.firebase.auth.FirebaseAuth -import com.google.firebase.storage.FirebaseStorage import dagger.hilt.android.AndroidEntryPoint -import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint class IntroductionFragment : UFragment() { - @Inject lateinit var firebaseAuth: FirebaseAuth - @Inject lateinit var firebaseStorage: FirebaseStorage @Inject lateinit var repository: SyncFrequencyRepository @Inject lateinit var preferences: SharedPreferences @@ -101,32 +95,10 @@ class IntroductionFragment : UFragment() { binding.textSelectCourseInternal.error = getString(R.string.error_select_a_course) } else { binding.textSelectCourseInternal.error = null - val user = firebaseAuth.currentUser - if (user != null) { - viewModel.uploadImageToStorage() - if (uefsStudent) { - viewModel.updateCourse(course, user) - } - } else { - Timber.d("Not connected to firebase. Write would denied") - } findNavController().navigate(R.id.action_introduction_to_configuration) } } - val current = firebaseAuth.currentUser - if (current != null) { - val reference = firebaseStorage.getReference("users/${current.uid}/avatar.jpg") - GlideApp.with(requireContext()) - .load(reference) - .fallback(R.drawable.ic_account_black_24dp) - .placeholder(R.drawable.ic_account_black_24dp) - .circleCrop() - .transition(DrawableTransitionOptions.withCrossFade()) - .signature(ObjectKey(System.currentTimeMillis() ushr 21)) - .into(binding.imageUserImage) - } - repository.getFrequencies().observe( viewLifecycleOwner ) { @@ -170,10 +142,10 @@ class IntroductionFragment : UFragment() { private fun onImagePicked(uri: Uri) { viewModel.setSelectedImage(uri) - GlideApp.with(requireContext()) + Glide.with(requireContext()) .load(uri) - .fallback(R.mipmap.ic_unes_large_image_512) - .placeholder(R.mipmap.ic_unes_large_image_512) + .fallback(com.forcetower.core.R.mipmap.ic_unes_large_image_512) + .placeholder(com.forcetower.core.R.mipmap.ic_unes_large_image_512) .circleCrop() .transition(DrawableTransitionOptions.withCrossFade()) .into(binding.imageUserImage) diff --git a/app/src/main/java/com/forcetower/uefs/feature/setup/SetupViewModel.kt b/app/src/main/java/com/forcetower/uefs/feature/setup/SetupViewModel.kt index c3ee35d5e..fc54945e8 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/setup/SetupViewModel.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/setup/SetupViewModel.kt @@ -26,18 +26,15 @@ import androidx.lifecycle.ViewModel import androidx.preference.PreferenceManager import com.forcetower.uefs.core.model.service.SyncFrequency import com.forcetower.uefs.core.model.unes.Course -import com.forcetower.uefs.core.storage.repository.FirebaseAuthRepository import com.forcetower.uefs.core.storage.repository.ProfileRepository import com.forcetower.uefs.core.work.image.UploadImageToStorage import com.forcetower.uefs.core.work.sync.SyncLinkedWorker import com.forcetower.uefs.core.work.sync.SyncMainWorker -import com.google.firebase.auth.FirebaseUser import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @HiltViewModel class SetupViewModel @Inject constructor( - private val firebaseAuthRepository: FirebaseAuthRepository, private val context: Context, private val profileRepository: ProfileRepository ) : ViewModel() { @@ -62,9 +59,8 @@ class SetupViewModel @Inject constructor( fun getSelectedCourse() = course - fun updateCourse(course: Course?, user: FirebaseUser) { + fun updateCourse(course: Course?) { course ?: return - firebaseAuthRepository.updateCourse(course, user) profileRepository.updateUserCourse(course) } @@ -77,7 +73,6 @@ class SetupViewModel @Inject constructor( } fun setFrequencyAndComplete(frequency: SyncFrequency) { - firebaseAuthRepository.updateFrequency(frequency.value) SyncLinkedWorker.stopWorker(context) SyncMainWorker.createWorker(context, frequency.value) PreferenceManager.getDefaultSharedPreferences(context).edit() diff --git a/app/src/main/java/com/forcetower/uefs/feature/setup/SyncSpecialFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/setup/SyncSpecialFragment.kt index 04293d179..bba30a51f 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/setup/SyncSpecialFragment.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/setup/SyncSpecialFragment.kt @@ -92,7 +92,7 @@ class SyncSpecialFragment : UFragment() { .setDefaultColorSchemeParams( CustomTabColorSchemeParams .Builder() - .setToolbarColor(ViewUtils.attributeColorUtils(requireContext(), R.attr.colorPrimary)) + .setToolbarColor(ViewUtils.attributeColorUtils(requireContext(), androidx.appcompat.R.attr.colorPrimary)) .build() ) .setShareState(CustomTabsIntent.SHARE_STATE_ON) diff --git a/app/src/main/java/com/forcetower/uefs/feature/shared/extensions/SnackbarHelpers.kt b/app/src/main/java/com/forcetower/uefs/feature/shared/extensions/SnackbarHelpers.kt index c10a21f69..c8782a808 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/shared/extensions/SnackbarHelpers.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/shared/extensions/SnackbarHelpers.kt @@ -39,7 +39,7 @@ fun Snackbar.config(pxElevation: Int = 6) { try { val at = view.findViewById(com.google.android.material.R.id.snackbar_action) - at.setTextColor(ViewUtils.attributeColorUtils(context, R.attr.colorPrimary)) + at.setTextColor(ViewUtils.attributeColorUtils(context, androidx.appcompat.R.attr.colorPrimary)) at.isAllCaps = false at.typeface = font } catch (ignored: Exception) {} diff --git a/app/src/main/java/com/forcetower/uefs/feature/themeswitcher/ThemeSwitcherResourceProvider.kt b/app/src/main/java/com/forcetower/uefs/feature/themeswitcher/ThemeSwitcherResourceProvider.kt index 0cfbe2ab5..e3c88ba5c 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/themeswitcher/ThemeSwitcherResourceProvider.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/themeswitcher/ThemeSwitcherResourceProvider.kt @@ -64,8 +64,8 @@ class ThemeSwitcherResourceProvider { @JvmStatic @StyleableRes private val PRIMARY_THEME_OVERLAY_ATTRS = intArrayOf( - R.attr.colorPrimary, - R.attr.colorPrimaryDark, + androidx.appcompat.R.attr.colorPrimary, + androidx.appcompat.R.attr.colorPrimaryDark, R.attr.colorPrimaryLight, R.attr.colorPrimaryAlpha ) @@ -73,16 +73,16 @@ class ThemeSwitcherResourceProvider { @JvmStatic @StyleableRes private val SECONDARY_THEME_OVERLAY_ATTRS = intArrayOf( - R.attr.colorAccent, - R.attr.colorSecondary + androidx.appcompat.R.attr.colorAccent, + com.google.android.material.R.attr.colorSecondary ) @JvmStatic @StyleableRes private val BACKGROUND_THEME_OVERLAY_ATTRS = intArrayOf( - R.attr.background, + androidx.appcompat.R.attr.background, R.attr.colorStatusBar, - R.attr.colorSurface, + com.google.android.material.R.attr.colorSurface, android.R.attr.windowBackground ) } diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/confirm/ConfirmEmailAccountFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/confirm/ConfirmEmailAccountFragment.kt new file mode 100644 index 000000000..66fd48b5a --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/confirm/ConfirmEmailAccountFragment.kt @@ -0,0 +1,84 @@ +package com.forcetower.uefs.feature.unesaccount.confirm + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import com.forcetower.core.extensions.closeKeyboard +import com.forcetower.core.extensions.openKeyboard +import com.forcetower.uefs.R +import com.forcetower.uefs.databinding.FragmentServiceAccountLinkEmailConfirmationBinding +import com.forcetower.uefs.feature.shared.UFragment +import com.forcetower.uefs.feature.unesaccount.confirm.vm.ConfirmEmailAccountEvent +import com.google.android.material.snackbar.Snackbar +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class ConfirmEmailAccountFragment : UFragment() { + private lateinit var binding: FragmentServiceAccountLinkEmailConfirmationBinding + private val viewModel by viewModels() + private val args by navArgs() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return FragmentServiceAccountLinkEmailConfirmationBinding.inflate(inflater, container, false).also { + binding = it + binding.email = args.email + binding.viewModel = viewModel + binding.lifecycleOwner = viewLifecycleOwner + }.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewModel.event.observe(viewLifecycleOwner, ::onEvent) + + binding.btnContinue.setOnClickListener { + val code = binding.code.text?.toString() ?: return@setOnClickListener + viewModel.submit(code, args.securityCode) + binding.btnContinue.closeKeyboard() + } + + binding.btnResend.setOnClickListener { + findNavController().popBackStack() + } + } + + private fun onEvent(event: ConfirmEmailAccountEvent) { + when (event) { + ConfirmEmailAccountEvent.Completed -> onCompleted() + ConfirmEmailAccountEvent.ConnectionFailed -> onConnectionFailed() + ConfirmEmailAccountEvent.EmailTaken -> onEmailTaken() + ConfirmEmailAccountEvent.InvalidCode -> onInvalidCode() + ConfirmEmailAccountEvent.TooManyTries -> onTooManyTries() + } + } + + private fun onCompleted() { + showSnack(getString(R.string.service_account_email_confirm_linked)) + findNavController().popBackStack(R.id.unes_account_overview, false) + } + + private fun onConnectionFailed() { + showSnack(getString(R.string.service_account_email_confirm_connection_failed)) + } + + private fun onEmailTaken() { + showSnack(getString(R.string.service_account_email_confirm_email_taken), Snackbar.LENGTH_LONG) + } + + private fun onInvalidCode() { + binding.codeLayout.error = getString(R.string.service_account_email_confirm_invalid_code) + binding.codeLayout.openKeyboard() + } + + private fun onTooManyTries() { + showSnack(getString(R.string.service_account_email_confirm_too_many_tries), Snackbar.LENGTH_LONG) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/confirm/ConfirmEmailAccountViewModel.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/confirm/ConfirmEmailAccountViewModel.kt new file mode 100644 index 000000000..7571afbb1 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/confirm/ConfirmEmailAccountViewModel.kt @@ -0,0 +1,37 @@ +package com.forcetower.uefs.feature.unesaccount.confirm + +import androidx.lifecycle.viewModelScope +import com.forcetower.core.lifecycle.viewmodel.BaseViewModel +import com.forcetower.uefs.core.model.ui.edge.EmailLinkComplete +import com.forcetower.uefs.domain.usecase.auth.LinkEmailUseCase +import com.forcetower.uefs.feature.unesaccount.confirm.vm.ConfirmEmailAccountEvent +import com.forcetower.uefs.feature.unesaccount.confirm.vm.ConfirmEmailAccountState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class ConfirmEmailAccountViewModel @Inject constructor( + private val linkEmail: LinkEmailUseCase +) : BaseViewModel(ConfirmEmailAccountState()) { + fun submit(code: String, securityToken: String) { + if (currentState.loading) return + if (code.isBlank()) return + + setState { it.copy(loading = true) } + + viewModelScope.launch { + val result = linkEmail.finish(code, securityToken) + val event = when (result) { + EmailLinkComplete.ConnectionError -> ConfirmEmailAccountEvent.ConnectionFailed + EmailLinkComplete.EmailTaken -> ConfirmEmailAccountEvent.EmailTaken + EmailLinkComplete.InvalidCode -> ConfirmEmailAccountEvent.InvalidCode + EmailLinkComplete.Linked -> ConfirmEmailAccountEvent.Completed + EmailLinkComplete.TooManyTries -> ConfirmEmailAccountEvent.TooManyTries + } + + sendEvent { event } + setState { it.copy(loading = false) } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/confirm/vm/ConfirmEmailAccountEvent.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/confirm/vm/ConfirmEmailAccountEvent.kt new file mode 100644 index 000000000..7bd6f6c59 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/confirm/vm/ConfirmEmailAccountEvent.kt @@ -0,0 +1,9 @@ +package com.forcetower.uefs.feature.unesaccount.confirm.vm + +sealed interface ConfirmEmailAccountEvent { + data object Completed : ConfirmEmailAccountEvent + data object InvalidCode : ConfirmEmailAccountEvent + data object EmailTaken : ConfirmEmailAccountEvent + data object ConnectionFailed : ConfirmEmailAccountEvent + data object TooManyTries : ConfirmEmailAccountEvent +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/confirm/vm/ConfirmEmailAccountState.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/confirm/vm/ConfirmEmailAccountState.kt new file mode 100644 index 000000000..6996e4a0b --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/confirm/vm/ConfirmEmailAccountState.kt @@ -0,0 +1,5 @@ +package com.forcetower.uefs.feature.unesaccount.confirm.vm + +data class ConfirmEmailAccountState( + val loading: Boolean = false +) diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/email/LinkEmailAccountFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/email/LinkEmailAccountFragment.kt new file mode 100644 index 000000000..12b0fc36d --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/email/LinkEmailAccountFragment.kt @@ -0,0 +1,69 @@ +package com.forcetower.uefs.feature.unesaccount.email + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import com.forcetower.core.extensions.closeKeyboard +import com.forcetower.uefs.R +import com.forcetower.uefs.databinding.FragmentServiceAccountLinkEmailBinding +import com.forcetower.uefs.feature.shared.UFragment +import com.forcetower.uefs.feature.unesaccount.email.vm.LinkEmailAccountEvent +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class LinkEmailAccountFragment : UFragment() { + private lateinit var binding: FragmentServiceAccountLinkEmailBinding + private val viewModel by viewModels() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return FragmentServiceAccountLinkEmailBinding.inflate(inflater, container, false).also { + binding = it + binding.viewModel = viewModel + binding.lifecycleOwner = viewLifecycleOwner + }.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewModel.event.observe(viewLifecycleOwner, ::onEvent) + + binding.btnContinue.setOnClickListener { + val email = binding.email.text?.toString() ?: return@setOnClickListener + viewModel.link(email) + binding.btnContinue.closeKeyboard() + } + } + + private fun onEvent(event: LinkEmailAccountEvent) { + when (event) { + is LinkEmailAccountEvent.EmailSent -> onEmailSent(event.token, event.email) + LinkEmailAccountEvent.InvalidEmail -> onInvalidEmail() + LinkEmailAccountEvent.InvalidInfo -> onInvalidInfo() + LinkEmailAccountEvent.SendError -> onSendError() + } + } + + private fun onEmailSent(token: String, email: String) { + val directions = LinkEmailAccountFragmentDirections.actionUnesAccountLinkEmailToUnesAccountConfirmEmail(token, email) + findNavController().navigate(directions) + } + + private fun onInvalidEmail() { + showSnack(getString(R.string.service_account_email_invalid_format)) + } + + private fun onInvalidInfo() { + showSnack(getString(R.string.service_account_email_send_invalid_info)) + } + + private fun onSendError() { + showSnack(getString(R.string.service_account_email_send_failed)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/email/LinkEmailAccountViewModel.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/email/LinkEmailAccountViewModel.kt new file mode 100644 index 000000000..a6ade26bb --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/email/LinkEmailAccountViewModel.kt @@ -0,0 +1,39 @@ +package com.forcetower.uefs.feature.unesaccount.email + +import android.util.Patterns +import androidx.lifecycle.viewModelScope +import com.forcetower.core.lifecycle.viewmodel.BaseViewModel +import com.forcetower.uefs.core.model.ui.edge.EmailLinkStart +import com.forcetower.uefs.domain.usecase.auth.LinkEmailUseCase +import com.forcetower.uefs.feature.unesaccount.email.vm.LinkEmailAccountEvent +import com.forcetower.uefs.feature.unesaccount.email.vm.LinkEmailAccountState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class LinkEmailAccountViewModel @Inject constructor( + private val linkEmail: LinkEmailUseCase +) : BaseViewModel(LinkEmailAccountState()) { + fun link(email: String) { + if (currentState.loading) return + + if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) { + sendEvent { LinkEmailAccountEvent.InvalidEmail } + return + } + + setState { it.copy(loading = true) } + + viewModelScope.launch { + val event = when (val result = linkEmail.start(email)) { + is EmailLinkStart.CodeSent -> LinkEmailAccountEvent.EmailSent(result.securityCode, email) + EmailLinkStart.ConnectionError -> LinkEmailAccountEvent.SendError + EmailLinkStart.InvalidInfo -> LinkEmailAccountEvent.InvalidInfo + } + + sendEvent { event } + setState { it.copy(loading = false) } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/email/vm/LinkEmailAccountEvent.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/email/vm/LinkEmailAccountEvent.kt new file mode 100644 index 000000000..e8dae6004 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/email/vm/LinkEmailAccountEvent.kt @@ -0,0 +1,8 @@ +package com.forcetower.uefs.feature.unesaccount.email.vm + +sealed interface LinkEmailAccountEvent { + data object InvalidEmail : LinkEmailAccountEvent + data class EmailSent(val token: String, val email: String) : LinkEmailAccountEvent + data object InvalidInfo : LinkEmailAccountEvent + data object SendError : LinkEmailAccountEvent +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/email/vm/LinkEmailAccountState.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/email/vm/LinkEmailAccountState.kt new file mode 100644 index 000000000..10c2ca8b4 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/email/vm/LinkEmailAccountState.kt @@ -0,0 +1,5 @@ +package com.forcetower.uefs.feature.unesaccount.email.vm + +data class LinkEmailAccountState( + val loading: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/login/LoginAccountFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/login/LoginAccountFragment.kt new file mode 100644 index 000000000..d05112d9f --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/login/LoginAccountFragment.kt @@ -0,0 +1,120 @@ +package com.forcetower.uefs.feature.unesaccount.login + +import android.os.Build +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.credentials.CredentialManager +import androidx.credentials.GetCredentialRequest +import androidx.credentials.GetCredentialResponse +import androidx.credentials.GetPasswordOption +import androidx.credentials.GetPublicKeyCredentialOption +import androidx.credentials.PasswordCredential +import androidx.credentials.PublicKeyCredential +import androidx.credentials.exceptions.GetCredentialException +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController +import com.forcetower.uefs.R +import com.forcetower.uefs.databinding.FragmentServiceAccountAccessLoginBinding +import com.forcetower.uefs.feature.shared.UFragment +import com.forcetower.uefs.feature.unesaccount.login.vm.LoginAccountEvent +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch +import timber.log.Timber + +@AndroidEntryPoint +class LoginAccountFragment : UFragment() { + private lateinit var binding: FragmentServiceAccountAccessLoginBinding + private val viewModel by viewModels() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return FragmentServiceAccountAccessLoginBinding.inflate(inflater, container, false).also { + binding = it + binding.viewModel = viewModel + binding.lifecycleOwner = viewLifecycleOwner + }.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewModel.event.observe(viewLifecycleOwner, ::onEvent) + + binding.btnContinue.setOnClickListener { + viewModel.anonymousLogin() + } + + binding.btnContinuePasskey.setOnClickListener { + viewModel.startPasskeyAssertion() + } + + binding.btnContinuePasskey.isVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P + } + + private fun onEvent(event: LoginAccountEvent) { + when (event) { + LoginAccountEvent.LoginFailed -> onLoginFailed() + LoginAccountEvent.SuccessHasEmail -> onLoginCompleted() + LoginAccountEvent.SuccessLinkEmail -> onLinkEmail() + is LoginAccountEvent.StartPasskeyAssertion -> onStartPasskeyAssertion(event) + } + } + + private fun onStartPasskeyAssertion(event: LoginAccountEvent.StartPasskeyAssertion) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return + val manager = CredentialManager.create(requireContext()) + + val publicKeyCredential = GetPublicKeyCredentialOption( + requestJson = event.json + ) + + val request = GetCredentialRequest(listOf(publicKeyCredential)) + + lifecycleScope.launch { + try { + val result = manager.getCredential( + context = requireActivity(), + request = request + ) + onCredentialSignInCompleted(result, event.flowId) + } catch (e: GetCredentialException) { + Timber.e(e, "Failed to get credentials") + viewModel.completePasskeyLoading() + onLoginFailed() + } + } + } + + private fun onCredentialSignInCompleted(result: GetCredentialResponse, flowId: String) { + when (val credential = result.credential) { + is PublicKeyCredential -> { + val responseJson = credential.authenticationResponseJson + viewModel.completePasskeyAssertion(flowId, responseJson) + } + else -> { + onLoginFailed() + viewModel.completePasskeyLoading() + } + } + } + + private fun onLoginCompleted() { + findNavController().popBackStack(R.id.unes_account_overview, false) + } + + private fun onLinkEmail() { + val direction = LoginAccountFragmentDirections.actionUnesAccountLoginToLinkEmailAccountFragment() + findNavController().navigate(direction) + } + + private fun onLoginFailed() { + showSnack(getString(R.string.service_account_login_anonymous_failed)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/login/LoginAccountViewModel.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/login/LoginAccountViewModel.kt new file mode 100644 index 000000000..cc23362ee --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/login/LoginAccountViewModel.kt @@ -0,0 +1,75 @@ +package com.forcetower.uefs.feature.unesaccount.login + +import androidx.lifecycle.viewModelScope +import com.forcetower.core.lifecycle.viewmodel.BaseViewModel +import com.forcetower.uefs.domain.usecase.auth.EdgeAnonymousLoginUseCase +import dagger.hilt.android.lifecycle.HiltViewModel +import com.forcetower.core.lifecycle.viewmodel.EventViewModel +import com.forcetower.uefs.domain.usecase.auth.CompleteAssertionUseCase +import com.forcetower.uefs.domain.usecase.auth.StartAssertionUseCase +import com.forcetower.uefs.feature.unesaccount.login.vm.LoginAccountEvent +import com.forcetower.uefs.feature.unesaccount.login.vm.LoginAccountState +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +@HiltViewModel +class LoginAccountViewModel @Inject constructor( + private val loginAnonymousLoginUseCase: EdgeAnonymousLoginUseCase, + private val startAssertionUseCase: StartAssertionUseCase, + private val completePasskeyAssertionUseCase: CompleteAssertionUseCase +) : BaseViewModel(LoginAccountState()) { + fun anonymousLogin() { + if (currentState.loading) return + setState { it.copy(loading = true) } + viewModelScope.launch { + runCatching { + loginAnonymousLoginUseCase.loginOrThrow() + }.onFailure { + Timber.e(it, "Failed to anonymously login") + sendEvent { LoginAccountEvent.LoginFailed } + }.onSuccess { user -> + if (user?.email != null) + sendEvent { LoginAccountEvent.SuccessHasEmail } + else + sendEvent { LoginAccountEvent.SuccessLinkEmail } + } + setState { it.copy(loading = false) } + } + } + + fun startPasskeyAssertion() { + if (currentState.loading) return + setState { it.copy(loading = true) } + viewModelScope.launch { + runCatching { + val result = startAssertionUseCase() + sendEvent { LoginAccountEvent.StartPasskeyAssertion(result.flowId, result.challenge) } + }.onFailure { + Timber.e(it, "Failed to start assertion") + sendEvent { LoginAccountEvent.LoginFailed } + } + } + } + + fun completePasskeyAssertion(flowId: String, json: String) { + viewModelScope.launch { + runCatching { + completePasskeyAssertionUseCase(flowId, json) + }.onFailure { + Timber.e(it, "Failed to complete assertion") + sendEvent { LoginAccountEvent.LoginFailed } + }.onSuccess { user -> + if (user?.email != null) + sendEvent { LoginAccountEvent.SuccessHasEmail } + else + sendEvent { LoginAccountEvent.SuccessLinkEmail } + } + setState { it.copy(loading = false) } + } + } + + fun completePasskeyLoading() { + setState { it.copy(loading = false) } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/login/vm/LoginAccountEvent.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/login/vm/LoginAccountEvent.kt new file mode 100644 index 000000000..45ab8e860 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/login/vm/LoginAccountEvent.kt @@ -0,0 +1,8 @@ +package com.forcetower.uefs.feature.unesaccount.login.vm + +sealed interface LoginAccountEvent { + data object SuccessHasEmail : LoginAccountEvent + data object SuccessLinkEmail : LoginAccountEvent + data object LoginFailed : LoginAccountEvent + data class StartPasskeyAssertion(val flowId: String, val json: String) : LoginAccountEvent +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/login/vm/LoginAccountState.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/login/vm/LoginAccountState.kt new file mode 100644 index 000000000..bc14d5177 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/login/vm/LoginAccountState.kt @@ -0,0 +1,5 @@ +package com.forcetower.uefs.feature.unesaccount.login.vm + +data class LoginAccountState( + val loading: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/overview/AccountOverviewFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/overview/AccountOverviewFragment.kt new file mode 100644 index 000000000..8074e6e4c --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/overview/AccountOverviewFragment.kt @@ -0,0 +1,210 @@ +package com.forcetower.uefs.feature.unesaccount.overview + +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi +import androidx.core.content.ContextCompat +import androidx.core.view.isVisible +import androidx.credentials.CreateCredentialResponse +import androidx.credentials.CreatePublicKeyCredentialRequest +import androidx.credentials.CreatePublicKeyCredentialResponse +import androidx.credentials.CredentialManager +import androidx.credentials.exceptions.CreateCredentialCancellationException +import androidx.credentials.exceptions.CreateCredentialException +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions +import com.canhub.cropper.CropImageContract +import com.canhub.cropper.CropImageContractOptions +import com.canhub.cropper.CropImageOptions +import com.canhub.cropper.CropImageView +import com.forcetower.core.adapters.imageUri +import com.forcetower.core.utils.ColorUtils +import com.forcetower.uefs.BuildConfig +import com.forcetower.uefs.R +import com.forcetower.uefs.core.model.edge.RegisterPasskeyStart +import com.forcetower.uefs.databinding.FragmentServiceAccountOverviewBinding +import com.forcetower.uefs.feature.shared.UFragment +import com.forcetower.uefs.feature.shared.getPixelsFromDp +import com.forcetower.uefs.feature.unesaccount.overview.vm.AccountOverviewEvent +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch +import timber.log.Timber + +@AndroidEntryPoint +class AccountOverviewFragment : UFragment() { + private lateinit var binding: FragmentServiceAccountOverviewBinding + private val viewModel by viewModels() + + private val pickImageContract = registerForActivityResult(ActivityResultContracts.GetContent()) { + onContentSelected(it) + } + + private val cropImage = registerForActivityResult(CropImageContract()) { + onCropResults(it) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + viewModel.fetch() + return FragmentServiceAccountOverviewBinding.inflate(inflater, container, false).also { + binding = it + binding.viewModel = viewModel + binding.lifecycleOwner = viewLifecycleOwner + }.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewModel.event.observe(viewLifecycleOwner, ::onEvent) + + binding.btnLogin.setOnClickListener { + onLoginStart() + } + + binding.btnAddEmail.setOnClickListener { + onLinkEmail() + } + + binding.btnCreatePasskey.setOnClickListener { + viewModel.registerPasskeyStart() + } + + binding.profileImage.setOnClickListener { + pickImage() + } + + binding.profileEmpty.setOnClickListener { + pickImage() + } + + binding.btnCreatePasskey.isVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P + } + + private fun onEvent(event: AccountOverviewEvent) { + when (event) { + is AccountOverviewEvent.PasskeyRegister -> onPasskeyRegisterStart(event) + AccountOverviewEvent.PasskeyRegisterConnectionFailed -> onConnectionFailed() + AccountOverviewEvent.ImageUpdateFailed -> onImageUpdateFailed() + } + } + + private fun onImageUpdateFailed() { + showSnack(getString(R.string.service_account_update_image_failed)) + imageUri(binding.profileImage, imageUrl = viewModel.user.value?.imageUrl, clipCircle = true) + } + + private fun onPasskeyRegisterStart(event: AccountOverviewEvent.PasskeyRegister) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return + + val manager = CredentialManager.create(requireContext()) + + Timber.d("Register challenge: ${event.json}") + + val request = CreatePublicKeyCredentialRequest( + requestJson = event.json, + preferImmediatelyAvailableCredentials = false, + ) + + lifecycleScope.launch { + try { + val result = manager.createCredential( + context = requireActivity(), + request = request, + ) + handlePasskeyRegistrationResult(result, event) + } catch (e: CreateCredentialCancellationException) { + showSnack(getString(R.string.service_account_register_passkey_credential_create_canceled)) + viewModel.onPasskeyRegistrationFinished() + } catch (e: CreateCredentialException) { + Timber.e(e, "Failed to create passkey") + showSnack(getString(R.string.service_account_register_passkey_credential_create_failed)) + viewModel.onPasskeyRegistrationFinished() + } + } + } + + private fun handlePasskeyRegistrationResult( + result: CreateCredentialResponse, + event: AccountOverviewEvent.PasskeyRegister + ) { + if (result is CreatePublicKeyCredentialResponse) { + Timber.d("Register Response: ${result.registrationResponseJson}") + viewModel.registerPasskeyFinish(event.flowId, result.registrationResponseJson) + } else { + showSnack(getString(R.string.service_account_register_passkey_credential_not_public)) + viewModel.onPasskeyRegistrationFinished() + } + } + + private fun onConnectionFailed() { + showSnack(getString(R.string.service_account_register_passkey_connect_failed)) + } + + private fun onLoginStart() { + val directions = AccountOverviewFragmentDirections.actionUnesAccountOverviewToUnesAccountStart() + findNavController().navigate(directions) + } + + private fun onLinkEmail() { + val directions = AccountOverviewFragmentDirections.actionUnesAccountOverviewToUnesAccountLinkEmail() + findNavController().navigate(directions) + } + + private fun onImagePicked(uri: Uri) { + Glide.with(requireContext()) + .load(uri) + .fallback(com.forcetower.core.R.mipmap.ic_unes_large_image_512) + .placeholder(com.forcetower.core.R.mipmap.ic_unes_large_image_512) + .circleCrop() + .transition(DrawableTransitionOptions.withCrossFade()) + .into(binding.profileImage) + + viewModel.uploadProfilePicture(uri) + } + + private fun pickImage() { + pickImageContract.launch("image/*") + } + + private fun onContentSelected(uri: Uri?) { + uri ?: return + val bg = ColorUtils.modifyAlpha(ContextCompat.getColor(requireContext(), R.color.colorPrimary), 120) + val ac = ContextCompat.getColor(requireContext(), R.color.colorAccent) + + val options = CropImageContractOptions( + uri, + CropImageOptions( + fixAspectRatio = true, + aspectRatioX = 1, + aspectRatioY = 1, + cropShape = CropImageView.CropShape.OVAL, + backgroundColor = bg, + borderLineColor = ac, + borderCornerColor = ac, + activityMenuIconColor = ac, + borderLineThickness = getPixelsFromDp(requireContext(), 2), + activityTitle = getString(R.string.cut_profile_image), + guidelines = CropImageView.Guidelines.OFF + ) + ) + + cropImage.launch(options) + } + + private fun onCropResults(result: CropImageView.CropResult) { + val imageUri = result.uriContent ?: return + onImagePicked(imageUri) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/overview/AccountOverviewViewModel.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/overview/AccountOverviewViewModel.kt new file mode 100644 index 000000000..939a5ded2 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/overview/AccountOverviewViewModel.kt @@ -0,0 +1,83 @@ +package com.forcetower.uefs.feature.unesaccount.overview + +import android.net.Uri +import androidx.lifecycle.asLiveData +import androidx.lifecycle.viewModelScope +import com.forcetower.core.lifecycle.viewmodel.BaseViewModel +import com.forcetower.uefs.domain.usecase.account.ChangeProfilePictureUseCase +import com.forcetower.uefs.domain.usecase.account.GetEdgeServiceAccountUseCase +import com.forcetower.uefs.domain.usecase.auth.RegisterPasskeyUseCase +import com.forcetower.uefs.domain.usecase.profile.GetProfileUseCase +import com.forcetower.uefs.feature.unesaccount.overview.vm.AccountOverviewEvent +import com.forcetower.uefs.feature.unesaccount.overview.vm.AccountOverviewState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +@HiltViewModel +class AccountOverviewViewModel @Inject constructor( + private val getAccount: GetEdgeServiceAccountUseCase, + private val registerPasskeyUseCase: RegisterPasskeyUseCase, + getProfile: GetProfileUseCase, + private val changeProfilePictureUseCase: ChangeProfilePictureUseCase +) : BaseViewModel(AccountOverviewState() +) { + val user = getAccount().asLiveData() + val profile = getProfile().asLiveData() + + fun fetch() { + viewModelScope.launch { + runCatching { + getAccount.update() + }.onFailure { + Timber.w(it, "Failed to update user") + } + } + } + + fun registerPasskeyStart() { + if (currentState.loading) return + setState { it.copy(loading = true) } + viewModelScope.launch { + runCatching { + val start = registerPasskeyUseCase.start() + sendEvent { AccountOverviewEvent.PasskeyRegister(start.flowId, start.create) } + }.onFailure { + Timber.e(it, "Failed to start registration") + sendEvent { AccountOverviewEvent.PasskeyRegisterConnectionFailed } + } + } + } + + fun registerPasskeyFinish(flowId: String, json: String) { + viewModelScope.launch { + runCatching { + registerPasskeyUseCase.finish(flowId, json) + }.onFailure { + Timber.e(it, "Failed to finish registration") + sendEvent { AccountOverviewEvent.PasskeyRegisterConnectionFailed } + } + + onPasskeyRegistrationFinished() + } + } + + fun onPasskeyRegistrationFinished() { + setState { it.copy(loading = false) } + } + + fun uploadProfilePicture(uri: Uri) { + setState { it.copy(uploadingPicture = true) } + viewModelScope.launch { + runCatching { + changeProfilePictureUseCase(uri) + }.onFailure { + Timber.e(it, "Failed to update image") + sendEvent { AccountOverviewEvent.ImageUpdateFailed } + } + } + + setState { it.copy(uploadingPicture = false) } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/overview/vm/AccountOverviewEvent.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/overview/vm/AccountOverviewEvent.kt new file mode 100644 index 000000000..2491bd441 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/overview/vm/AccountOverviewEvent.kt @@ -0,0 +1,7 @@ +package com.forcetower.uefs.feature.unesaccount.overview.vm + +sealed interface AccountOverviewEvent { + data object PasskeyRegisterConnectionFailed : AccountOverviewEvent + data object ImageUpdateFailed : AccountOverviewEvent + data class PasskeyRegister(val flowId: String, val json: String): AccountOverviewEvent +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/overview/vm/AccountOverviewState.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/overview/vm/AccountOverviewState.kt new file mode 100644 index 000000000..cf9f26ead --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/overview/vm/AccountOverviewState.kt @@ -0,0 +1,6 @@ +package com.forcetower.uefs.feature.unesaccount.overview.vm + +data class AccountOverviewState( + val loading: Boolean = false, + val uploadingPicture: Boolean = false +) diff --git a/app/src/main/java/com/forcetower/uefs/feature/unesaccount/start/CreateAccountStartFragment.kt b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/start/CreateAccountStartFragment.kt new file mode 100644 index 000000000..66cfcd999 --- /dev/null +++ b/app/src/main/java/com/forcetower/uefs/feature/unesaccount/start/CreateAccountStartFragment.kt @@ -0,0 +1,37 @@ +package com.forcetower.uefs.feature.unesaccount.start + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.navigation.fragment.findNavController +import com.forcetower.uefs.databinding.FragmentServiceAccountCreateStartBinding +import com.forcetower.uefs.feature.shared.UFragment +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class CreateAccountStartFragment : UFragment() { + private lateinit var binding: FragmentServiceAccountCreateStartBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return FragmentServiceAccountCreateStartBinding.inflate(inflater, container, false).also { + binding = it + }.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.btnContinue.setOnClickListener { + val directions = CreateAccountStartFragmentDirections.actionUnesAccountStartToUnesAccountLogin() + findNavController().navigate(directions) + } + + binding.btnWhy.setOnClickListener { + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/forcetower/uefs/feature/web/KeepAliveService.kt b/app/src/main/java/com/forcetower/uefs/feature/web/KeepAliveService.kt index 5cd979a85..527f664de 100644 --- a/app/src/main/java/com/forcetower/uefs/feature/web/KeepAliveService.kt +++ b/app/src/main/java/com/forcetower/uefs/feature/web/KeepAliveService.kt @@ -27,7 +27,7 @@ import android.os.IBinder class KeepAliveService : Service() { - override fun onBind(intent: Intent): IBinder? { + override fun onBind(intent: Intent): IBinder { return sBinder } diff --git a/app/src/main/java/com/forcetower/uefs/service/NotificationCreator.kt b/app/src/main/java/com/forcetower/uefs/service/NotificationCreator.kt index 975f191f9..c2cdc354f 100644 --- a/app/src/main/java/com/forcetower/uefs/service/NotificationCreator.kt +++ b/app/src/main/java/com/forcetower/uefs/service/NotificationCreator.kt @@ -32,7 +32,7 @@ import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat import androidx.preference.PreferenceManager -import com.forcetower.uefs.GlideApp +import com.bumptech.glide.Glide import com.forcetower.uefs.R import com.forcetower.uefs.core.constants.Constants import com.forcetower.uefs.core.model.bigtray.BigTrayData @@ -416,7 +416,7 @@ object NotificationCreator { private fun createBigImage(context: Context, image: String): NotificationCompat.Style? { return try { - val bitmap = GlideApp.with(context).asBitmap().load(image).submit().get() + val bitmap = Glide.with(context).asBitmap().load(image).submit().get() NotificationCompat.BigPictureStyle().bigPicture(bitmap) } catch (t: Throwable) { Timber.d("Error happened at image load: ${t.message}") diff --git a/app/src/main/java/com/forcetower/uefs/widget/BottomSheetBehavior.kt b/app/src/main/java/com/forcetower/uefs/widget/BottomSheetBehavior.kt index c8b6639ae..f1ca1dee2 100644 --- a/app/src/main/java/com/forcetower/uefs/widget/BottomSheetBehavior.kt +++ b/app/src/main/java/com/forcetower/uefs/widget/BottomSheetBehavior.kt @@ -258,21 +258,21 @@ class BottomSheetBehavior : Behavior { @SuppressLint("PrivateResource") constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { // Re-use BottomSheetBehavior's attrs - val a = context.obtainStyledAttributes(attrs, R.styleable.BottomSheetBehavior_Layout) - val value = a.peekValue(R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight) + val a = context.obtainStyledAttributes(attrs, com.google.android.material.R.styleable.BottomSheetBehavior_Layout) + val value = a.peekValue(com.google.android.material.R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight) peekHeight = if (value != null && value.data == PEEK_HEIGHT_AUTO) { value.data } else { a.getDimensionPixelSize( - R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight, + com.google.android.material.R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight, PEEK_HEIGHT_AUTO ) } - isHideable = a.getBoolean(R.styleable.BottomSheetBehavior_Layout_behavior_hideable, false) + isHideable = a.getBoolean(com.google.android.material.R.styleable.BottomSheetBehavior_Layout_behavior_hideable, false) isFitToContents = - a.getBoolean(R.styleable.BottomSheetBehavior_Layout_behavior_fitToContents, true) + a.getBoolean(com.google.android.material.R.styleable.BottomSheetBehavior_Layout_behavior_fitToContents, true) skipCollapsed = - a.getBoolean(R.styleable.BottomSheetBehavior_Layout_behavior_skipCollapsed, false) + a.getBoolean(com.google.android.material.R.styleable.BottomSheetBehavior_Layout_behavior_skipCollapsed, false) a.recycle() val configuration = ViewConfiguration.get(context) minimumVelocity = configuration.scaledMinimumFlingVelocity @@ -349,7 +349,7 @@ class BottomSheetBehavior : Behavior { // init peekHeightMin @SuppressLint("PrivateResource") peekHeightMin = parent.resources.getDimensionPixelSize( - R.dimen.design_bottom_sheet_peek_height_min + com.google.android.material.R.dimen.design_bottom_sheet_peek_height_min ) } lastPeekHeight = max(peekHeightMin, parentHeight - parent.width * 9 / 16) diff --git a/app/src/main/java/com/forcetower/uefs/widget/IconShadowedView.kt b/app/src/main/java/com/forcetower/uefs/widget/IconShadowedView.kt index 2789535d6..874e45a1f 100644 --- a/app/src/main/java/com/forcetower/uefs/widget/IconShadowedView.kt +++ b/app/src/main/java/com/forcetower/uefs/widget/IconShadowedView.kt @@ -42,6 +42,7 @@ import androidx.annotation.StyleRes import androidx.interpolator.view.animation.FastOutSlowInInterpolator import com.forcetower.uefs.R +@Suppress("DEPRECATION") class IconShadowedView @JvmOverloads constructor( context: Context, attrs: AttributeSet, diff --git a/app/src/main/java/com/forcetower/uefs/widget/NumberPicker.java b/app/src/main/java/com/forcetower/uefs/widget/NumberPicker.java index 8dc01c67d..edb556445 100644 --- a/app/src/main/java/com/forcetower/uefs/widget/NumberPicker.java +++ b/app/src/main/java/com/forcetower/uefs/widget/NumberPicker.java @@ -2182,7 +2182,7 @@ public CustomEditText(Context context, AttributeSet attrs) { super(context, attrs); TypedValue typedValue = new TypedValue(); Resources.Theme theme = context.getTheme(); - theme.resolveAttribute(R.attr.colorOnSurface, typedValue, true); + theme.resolveAttribute(com.google.android.material.R.attr.colorOnSurface, typedValue, true); setTextColor(typedValue.data); float textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 7, context.getResources().getDisplayMetrics()); diff --git a/app/src/main/res/drawable/baseline_security_24.xml b/app/src/main/res/drawable/baseline_security_24.xml new file mode 100644 index 000000000..50bee5791 --- /dev/null +++ b/app/src/main/res/drawable/baseline_security_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/baseline_verified_24.xml b/app/src/main/res/drawable/baseline_verified_24.xml new file mode 100644 index 000000000..bc159036d --- /dev/null +++ b/app/src/main/res/drawable/baseline_verified_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/outline_account_circle_24.xml b/app/src/main/res/drawable/outline_account_circle_24.xml new file mode 100644 index 000000000..fdd35c026 --- /dev/null +++ b/app/src/main/res/drawable/outline_account_circle_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/outline_edit_24.xml b/app/src/main/res/drawable/outline_edit_24.xml new file mode 100644 index 000000000..e59da99ed --- /dev/null +++ b/app/src/main/res/drawable/outline_edit_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/passkey_baseline_24.xml b/app/src/main/res/drawable/passkey_baseline_24.xml new file mode 100644 index 000000000..39c43b0a6 --- /dev/null +++ b/app/src/main/res/drawable/passkey_baseline_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/unes_colored_logo.xml b/app/src/main/res/drawable/unes_colored_logo.xml new file mode 100644 index 000000000..cee14037e --- /dev/null +++ b/app/src/main/res/drawable/unes_colored_logo.xml @@ -0,0 +1,438 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_adventure_begins.xml b/app/src/main/res/layout/fragment_adventure_begins.xml index f95268991..58f5d7c7e 100644 --- a/app/src/main/res/layout/fragment_adventure_begins.xml +++ b/app/src/main/res/layout/fragment_adventure_begins.xml @@ -23,14 +23,6 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> - - - - diff --git a/app/src/main/res/layout/fragment_discipline_old.xml b/app/src/main/res/layout/fragment_discipline_old.xml index 647a2341c..a8213beb2 100644 --- a/app/src/main/res/layout/fragment_discipline_old.xml +++ b/app/src/main/res/layout/fragment_discipline_old.xml @@ -26,13 +26,6 @@ - - - + type="com.forcetower.uefs.core.model.unes.EdgeServiceAccount" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + +